diff --git a/branches/ph-plugins/Assistant/Assistant.cs b/branches/ph-plugins/Assistant/Assistant.cs
new file mode 100644
index 000000000..880edd639
--- /dev/null
+++ b/branches/ph-plugins/Assistant/Assistant.cs
@@ -0,0 +1,341 @@
+/*
+ * Process Hacker Assistant
+ *
+ * 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.Runtime.InteropServices;
+using ProcessHacker.Common;
+using ProcessHacker.Native.Api;
+using ProcessHacker.Native.Objects;
+using ProcessHacker.Native.Security;
+using ProcessHacker.Native.Security.AccessControl;
+
+namespace Assistant
+{
+ static class Program
+ {
+ static void SetDesktopWinStaAccess()
+ {
+ using (var wsHandle = new WindowStationHandle("WinSta0", (WindowStationAccess)StandardRights.WriteDac))
+ wsHandle.SetSecurity(SecurityInformation.Dacl, new SecurityDescriptor());
+
+ using (var dhandle = new DesktopHandle("Default", false,
+ (DesktopAccess)StandardRights.WriteDac | DesktopAccess.ReadObjects | DesktopAccess.WriteObjects))
+ dhandle.SetSecurity(SecurityInformation.Dacl, new SecurityDescriptor());
+ }
+
+ 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;
+ }
+
+ static void PrintUsage()
+ {
+ Console.Write("Process Hacker Assistant\nCopyright (c) 2008 wj32. Licensed under the GNU GPL v3.\n\nUsage:\n" +
+ "\tassistant [-w] [-k] [-P pid] [-u username] [-p password] [-t logontype] [-s sessionid] [-d dir] " +
+ "[-c cmdline] [-f filename] [-E name]\n\n" +
+ "-w\t\tSpecifies that the permissions of WinSta0 and WinSta0\\Default should be " +
+ "modified with all access. You should use this option as a normal user (\"assistant -w\") before attempting to " +
+ "use this program as a Windows service.\n" +
+ "-k\t\tDebugging purposes: specifies that this program should sleep after completion.\n" +
+ "-P pid\t\t\"Steals\" the token of the specified process to start the specified program. You must not use " +
+ "the -u and -p options with this option.\n" +
+ "-u username\tSpecifies the user under which the program should be run. The username can be specified " +
+ "as username, domain\\username, or username@domain. On Windows XP, specifying NT AUTHORITY\\SYSTEM does " +
+ "not work by itself. You must specify \"-t newcredentials\" as well.\n" +
+ "-p password\tSpecifies the password for the user.\n" +
+ "-t logontype\tSpecifies the logon type. For logons to normal users, specify \"interactive\". For logons " +
+ "to NT AUTHORITY\\SYSTEM, LOCAL SERVICE or NETWORK SERVICE, specify \"service\" (see above for using SYSTEM on " +
+ "Windows XP).\n" +
+ "-s sessionid\tSpecifies the session ID under which the program should be run.\n" +
+ "-d dir\t\tSpecifies the current directory for the program.\n" +
+ "-c cmdline\tSpecifies the command line for the program. You must not use the -f option if you use this.\n" +
+ "-f filename\tSpecifies the full path to the program.\n" +
+ "-E name\tSpecifies the partial name of the mailslot to write a 4-byte error code to.\n" +
+ "\n" +
+ "This application is not useful by itself; even Administrators do not normally have " +
+ "SeAssignPrimaryTokenPrivilege and SeTcbPrivilege, both of which are required for the useful " +
+ "functioning of this program. You must create a Windows service for this program:\n" +
+ "\tsc.exe create PHAssistant binPath= \"\\\"[path to this program]\\\" -u \\\"SYSTEM@NT AUTHORITY\\\" " +
+ "-t service -s [your session Id, normally 0 on XP and 1 on Vista] -c calc.exe\"\n" +
+ "then start it:\n\tsc.exe start PHAssistant\n" +
+ "and finally delete it:\n\tsc.exe delete PHAssistant\n");
+ }
+
+ static void Exit(int exitCode)
+ {
+ if (args.ContainsKey("-k"))
+ System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);
+
+ if (args.ContainsKey("-E"))
+ {
+ string mailslotName = args["-E"];
+
+ using (var fhandle = new FileHandle(
+ @"\Device\Mailslot\" + mailslotName,
+ FileShareMode.ReadWrite,
+ FileAccess.GenericWrite
+ ))
+ fhandle.Write(exitCode.GetBytes());
+ }
+
+ Environment.Exit(exitCode);
+ }
+
+ static void Exit()
+ {
+ Exit(0);
+ }
+
+ static Dictionary args;
+
+ static bool EnablePrivilege(string name)
+ {
+ try
+ {
+ Privilege.Enable(name);
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ static void Main()
+ {
+ EnablePrivilege("SeAssignPrimaryTokenPrivilege");
+ EnablePrivilege("SeBackupPrivilege");
+ EnablePrivilege("SeRestorePrivilege");
+
+ try
+ {
+ args = ParseArgs(Environment.GetCommandLineArgs());
+
+ bool bad = false;
+
+ if (!args.ContainsKey("-w"))
+ {
+ if (!args.ContainsKey("-c") && !args.ContainsKey("-f"))
+ bad = true;
+
+ if (args.ContainsKey("-c") && args.ContainsKey("-f"))
+ bad = true;
+
+ if (!args.ContainsKey("-u") && !args.ContainsKey("-P"))
+ bad = true;
+
+ if (args.ContainsKey("-u") && args.ContainsKey("-P"))
+ bad = true;
+ }
+
+ if (args.ContainsKey("-v") || args.ContainsKey("-h"))
+ bad = true;
+
+ if (bad)
+ {
+ PrintUsage();
+ Exit();
+ }
+ }
+ catch
+ {
+ PrintUsage();
+ Exit();
+ }
+
+ if (args.ContainsKey("-w"))
+ {
+ try
+ {
+ SetDesktopWinStaAccess();
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("Warning: Could not set desktop and window station access: " + ex.Message);
+ }
+ }
+
+ IntPtr token = IntPtr.Zero;
+ string domain = null;
+ string username = "";
+
+ if (args.ContainsKey("-u"))
+ {
+ string user = args["-u"];
+
+ if (user.Contains("\\"))
+ {
+ domain = user.Split('\\')[0];
+ username = user.Split('\\')[1];
+ }
+ else if (user.Contains("@"))
+ {
+ username = user.Split('@')[0];
+ domain = user.Split('@')[1];
+ }
+ else
+ {
+ username = user;
+ }
+
+ LogonType type = LogonType.Interactive;
+
+ if (args.ContainsKey("-t"))
+ {
+ try
+ {
+ type = (LogonType)Enum.Parse(typeof(LogonType), args["-t"], true);
+ }
+ catch
+ {
+ Console.WriteLine("Error: Invalid logon type.");
+ Exit(-1);
+ }
+ }
+
+ if (!Win32.LogonUser(username, domain, args.ContainsKey("-p") ? args["-p"] : "", type,
+ LogonProvider.Default, out token))
+ {
+ Console.WriteLine("Error: Could not logon as user: " + Win32.GetLastErrorMessage());
+ Exit(Marshal.GetLastWin32Error());
+ }
+ }
+ else
+ {
+ int pid = System.Diagnostics.Process.GetCurrentProcess().Id;
+
+ try
+ {
+ if (args.ContainsKey("-P"))
+ pid = int.Parse(args["-P"]);
+ }
+ catch
+ {
+ Console.WriteLine("Error: Invalid PID.");
+ }
+
+ IntPtr handle = IntPtr.Zero;
+
+ try
+ {
+ handle = System.Diagnostics.Process.GetProcessById(pid).Handle;
+ }
+ catch
+ {
+ Console.WriteLine("Error: Could not open process.");
+ }
+
+
+ if (!Win32.OpenProcessToken(handle, TokenAccess.All, out token))
+ {
+ Console.WriteLine("Error: Could not open process token: " + Win32.GetLastErrorMessage());
+ Exit(Marshal.GetLastWin32Error());
+ }
+
+ if (Environment.OSVersion.Version.Major != 5)
+ {
+ IntPtr dupToken;
+
+ if (!Win32.DuplicateTokenEx(token, TokenAccess.All, IntPtr.Zero, SecurityImpersonationLevel.SecurityImpersonation,
+ TokenType.Primary, out dupToken))
+ {
+ Console.WriteLine("Error: Could not duplicate own token: " + Win32.GetLastErrorMessage());
+ Exit(Marshal.GetLastWin32Error());
+ }
+
+ Win32.CloseHandle(token);
+ token = dupToken;
+ }
+ }
+
+ if (args.ContainsKey("-s"))
+ {
+ int sessionId = int.Parse(args["-s"]);
+
+ if (!Win32.SetTokenInformation(token, TokenInformationClass.TokenSessionId, ref sessionId, 4))
+ {
+ Console.WriteLine("Error: Could not set token session Id: " + Win32.GetLastErrorMessage());
+ }
+ }
+
+ if (args.ContainsKey("-c") || args.ContainsKey("-f"))
+ {
+ if (!args.ContainsKey("-e"))
+ {
+ StartupInfo info = new StartupInfo();
+ ProcessInformation pinfo = new ProcessInformation();
+ IntPtr environment;
+
+ Win32.CreateEnvironmentBlock(out environment, token, false);
+
+ info.Size = Marshal.SizeOf(info);
+ info.Desktop = "WinSta0\\Default";
+
+ if (!Win32.CreateProcessAsUser(token,
+ args.ContainsKey("-f") ? args["-f"] : null,
+ args.ContainsKey("-c") ? args["-c"] : null,
+ IntPtr.Zero, IntPtr.Zero, false, ProcessCreationFlags.CreateUnicodeEnvironment, environment,
+ args.ContainsKey("-d") ? args["-d"] : null,
+ ref info, out pinfo))
+ {
+ Console.WriteLine("Error: Could not create process: " + Win32.GetLastErrorMessage());
+ Exit(Marshal.GetLastWin32Error());
+ }
+
+ Win32.CloseHandle(token);
+ }
+ }
+
+ Exit();
+ }
+ }
+}
diff --git a/branches/ph-plugins/Assistant/Assistant.csproj b/branches/ph-plugins/Assistant/Assistant.csproj
new file mode 100644
index 000000000..46724f8ff
--- /dev/null
+++ b/branches/ph-plugins/Assistant/Assistant.csproj
@@ -0,0 +1,87 @@
+
+
+
+ Debug
+ AnyCPU
+ 9.0.30729
+ 2.0
+ {0710ADEF-F89E-4CBC-8150-B340460BC9D6}
+ Exe
+ Properties
+ Assistant
+ Assistant
+ v2.0
+ 512
+ Assistant.Program
+ app.manifest
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ AnyCPU
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ AnyCPU
+
+
+
+
+
+
+
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+ Designer
+
+
+ True
+ Resources.resx
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+ True
+ Settings.settings
+ True
+
+
+
+
+ {8E10F5E8-D4FA-4980-BB23-2EDD134AC15E}
+ ProcessHacker.Common
+
+
+ {8A448157-E1A7-4DDF-954E-287F1117832B}
+ ProcessHacker.Native
+
+
+
+
+
+ mkdir "$(SolutionDir)\ProcessHacker\$(OutDir)"
+copy "$(TargetPath)" "$(SolutionDir)\ProcessHacker\$(OutDir)"
+copy "$(TargetDir)\Assistant.pdb" "$(SolutionDir)\ProcessHacker\$(OutDir)"
+
+
\ No newline at end of file
diff --git a/branches/ph-plugins/Assistant/Properties/AssemblyInfo.cs b/branches/ph-plugins/Assistant/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..e3968b1c7
--- /dev/null
+++ b/branches/ph-plugins/Assistant/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Process Hacker Assistant")]
+[assembly: AssemblyDescription("Process Hacker Assistant")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("wj32")]
+[assembly: AssemblyProduct("Process Hacker")]
+[assembly: AssemblyCopyright("Licensed under the GNU GPL.")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("10a62c48-2a7a-4e76-9103-ac46d4d55ea8")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.6.0.0")]
+[assembly: AssemblyFileVersion("1.6.0.0")]
diff --git a/branches/ph-plugins/Assistant/Properties/Resources.Designer.cs b/branches/ph-plugins/Assistant/Properties/Resources.Designer.cs
new file mode 100644
index 000000000..1eae069fc
--- /dev/null
+++ b/branches/ph-plugins/Assistant/Properties/Resources.Designer.cs
@@ -0,0 +1,71 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:2.0.50727.1434
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace Assistant.Properties
+{
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources
+ {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources()
+ {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager
+ {
+ get
+ {
+ if ((resourceMan == null))
+ {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Assistant.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture
+ {
+ get
+ {
+ return resourceCulture;
+ }
+ set
+ {
+ resourceCulture = value;
+ }
+ }
+ }
+}
diff --git a/branches/ph-plugins/Assistant/Properties/Resources.resx b/branches/ph-plugins/Assistant/Properties/Resources.resx
new file mode 100644
index 000000000..ffecec851
--- /dev/null
+++ b/branches/ph-plugins/Assistant/Properties/Resources.resx
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/branches/ph-plugins/Assistant/Properties/Settings.Designer.cs b/branches/ph-plugins/Assistant/Properties/Settings.Designer.cs
new file mode 100644
index 000000000..dc7bdcb9a
--- /dev/null
+++ b/branches/ph-plugins/Assistant/Properties/Settings.Designer.cs
@@ -0,0 +1,30 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:2.0.50727.1434
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace Assistant.Properties
+{
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "9.0.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
+ {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default
+ {
+ get
+ {
+ return defaultInstance;
+ }
+ }
+ }
+}
diff --git a/branches/ph-plugins/Assistant/Properties/Settings.settings b/branches/ph-plugins/Assistant/Properties/Settings.settings
new file mode 100644
index 000000000..abf36c5d3
--- /dev/null
+++ b/branches/ph-plugins/Assistant/Properties/Settings.settings
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/branches/ph-plugins/Assistant/app.manifest b/branches/ph-plugins/Assistant/app.manifest
new file mode 100644
index 000000000..eb3c3ae01
--- /dev/null
+++ b/branches/ph-plugins/Assistant/app.manifest
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/branches/ph-plugins/CHANGELOG.txt b/branches/ph-plugins/CHANGELOG.txt
new file mode 100644
index 000000000..9b169170b
--- /dev/null
+++ b/branches/ph-plugins/CHANGELOG.txt
@@ -0,0 +1,725 @@
+Process Hacker
+
+1.6
+ * NEW/IMPROVED:
+ * #2817429 - "Add Port and IP Address columns to Network tab"
+ * #2845829 - "System uptime"
+ * #2853452 - "Find handles window: Minor usability improvements"
+ * Update system
+ * Network tools: ping, traceroute and whois
+ * Object security editor
+ * Displays IPv6 network connections
+ * VirusTotal uploader
+ * Two new terminator tests: W1 (send WM_DESTROY messages) and
+ W2 (send WM_QUIT messages)
+ * Wait chain analysis
+ * Mutant owner information
+ * Elevation prompt when attempting to view process properties
+ * All list views now have sort arrows
+ * Network list now displays process tooltips
+ * Enabled DLL injection into processes from other sessions
+ * Decreased memory and CPU usage
+ * Added a glossary to Help
+ * Configurable elevation prompts
+ * Better Windows 7 user interface support
+ * Exception reporting from within Process Hacker
+ * Slimmer thread call stack window
+ * FIXED:
+ * #2820170 - "System.ArgumentOutOfRangeException in network list"
+ * #2845427 - "Indicator integer overflow"
+ * #2847691 - "Apply button bug"
+ * #2849052 - "not defined by current visual style"
+ * #2863305 - "Unhandled exception in comparer"
+ * Critical KProcessHacker denial-of-service security issues
+ * Crashes within the process tree model
+ * Incorrect menu items for System threads
+ * Crash when viewing error details in the event
+ of a corrupt configuration file
+ * Notification icons were fixed at 16x16 size
+ * Sort order for the relative start time column
+ * Incorrect process priority class display
+ * Commit and Physical Memory History icon colors
+ * Packed file detection, PE reader
+ * Performance history buffer problems
+ * REMOVED:
+ * Messages in the main window's status bar
+
+1.5
+ * NEW/IMPROVED:
+ * #2831605 - "Add handle count by type to process properties handle tab"
+ * #2836706 - "Signature Column in Processes"
+ * Improved kernel modules list
+ * Detects custom kernels
+ * Performance improvements
+ * KTM resource manager information
+ * FIXED:
+ * Windows XP BSODs
+ * Incorrect drive letter resolving for file handles
+ * Linked token display on x64
+
+1.4
+ * NEW/IMPROVED:
+ * Full support for Windows 7 SP0
+ * Basic support for Windows 64-bit
+ * Ability to unload drivers
+ * Handle names for Kernel Transaction Manager (KTM) objects
+ * Ability to save details for processes
+ * Improved handle granted access display
+ * Improved process window exit status display
+ * Improved user prompts
+ * Ability to open key handles in regedit
+ * Thread list is more responsive
+ * Process exit notification (in the process window) is now instant
+ * Improved control tab indicies
+ * Small performance improvements
+ * FIXED:
+ * #2821437 - "Windows 7 PsTerminateProcess crash"
+ * #2834578 - "Unable to replace Task Manager with Process Hacker error"
+ * Properties menu item for handles was disabled most of the time
+ * Handle names could not be viewed properly without KPH and
+ on systems without the VC++ 9 runtime
+ * Minor KPH pool leak
+ * Annoying popup when Process Hacker replaces Task Manager on
+ Windows 7
+ * No symbols for protected processes
+
+1.3.9.0
+ * NEW/IMPROVED:
+ * #2812814 - "Auto-scroll option should be remembered"
+ * Kernel-mode stack traces
+ * POSIX process support, including command lines and
+ highlighting
+ * Hidden processes scanner can now detect FUTo
+ * Highlighting for .NET and relocated DLLs
+ * Ability to terminate system threads
+ * Ability to force terminate threads
+ * Ability to create services
+ * Ability to set DEP status of processes in other sessions
+ * Ability to unload modules of processes in other sessions
+ * Ability to dump memory to a file
+ * Process window manipulation
+ * Process heap information
+ * Paged and non-paged pool limit display
+ * Terminator test: TP1a (TP1, alternative method)
+ * Terminator test: TT1a (TT1, alternative method)
+ * Terminator test: TT4 (dangerous thread termination)
+ * Better file object names without KProcessHacker
+ * Better IP address resolving
+ * CPU, I/O and memory indicators
+ * Child windows float by default
+ * dbghelp is now set up automatically when Process Hacker is
+ run the first time
+ * Thread wait analysis now detects NtQueryObject hangs and
+ named pipe connections
+ * Automatic tree text coloring, allowing for dark highlighting
+ colors
+ * Small performance improvements
+ * FIXED:
+ * #2811733 - "System.NullReferenceException"
+ * Broken system thread start addresses due to sign-extending
+ instead of zero-extending pointers
+ * Broken Ctrl+A for memory, results, and PE lists
+ * Removed several annoying thread-related warnings
+ * Network connections now display process IDs
+ * Ctrl+C in the log window
+ * Window location problem when hiding and restoring the window
+ * ObjectDisposedExceptions when closing search options windows
+ * "Inject DLL" menu item enabling problem
+ * InvalidCastException when attempting to close handles in
+ the handle filter window
+
+1.3.8.5
+ * NEW/IMPROVED:
+ * Full support for Windows Vista SP2
+ * Users/sessions list
+ * Window process finder
+ * Thread wait analysis - right-click a thread and choose
+ Analyze > Wait to see what a thread is hanging on
+ * Added ability to create dump files for processes
+ * Added ability to detach processes from debuggers
+ * Added "scroll down process tree on startup" option
+ * Notification icon process list is now sorted
+ * Lists are dramatically faster (especially the handle list)
+ * Detailed handle properties
+ * Event objects can now be modified - set, clear, pulse, reset
+ * Event pair objects can now be modified - set high, set low
+ * Semaphore objects can now be modified - acquire, release
+ * Statistics for token objects
+ * Token object names now include their session LUIDs
+ * Added Shift+Del for Terminate Process Tree
+ * FIXED:
+ * #2795871 - "Hidden Processes window resizing problem"
+ * #2800710 - "System.ObjectDisposedException"
+ * Windows 7 RC BSOD (Windows 7 Beta is no longer supported) at
+ startup; support is STILL EXPERIMENTAL
+ * Memory search addresses being in decimal
+ * Disabling "Warn about dangerous actions" now disables all
+ process-related prompts
+ * Terminator window would be hidden if the main window was top-most
+ * Using the keyboard (Up/Down/Left/Right) in the process list was fixed
+ * Potential BSOD with KphReadVirtualMemory and KphWriteVirtualMemory
+ due to incorrect address probing
+ * Get Function Address window would return incorrect hex addresses
+
+1.3.8.0
+ * NEW/IMPROVED:
+ * KProcessHacker can now perform process memory reading/writing
+ by itself and does not require MmCopyVirtualMemory
+ * KProcessHacker can now bypass all handle-opening protections
+ * Experimental process protection feature
+ * Ability to set handle flags such as protect-from-close and inherit
+ * Better highlighting
+ * Terminator test: TD1 (debugs a process and closes the debug object)
+ * Terminator test: TT3 (TT1 is now completely user-mode)
+ * Shows function file and line numbers where available
+ * Icon updating is now done on the shared thread to avoid the GUI
+ blocking when explorer.exe is suspended or is hanging
+ * FIXED:
+ * #2785648 - "cursor down crashes PH"
+ * #2790404 - "System.InvalidOperationException"
+ * Incomplete or inaccurate thread call stacks
+ * Windows 7 BSOD
+ * Crash upon executing terminator test M1
+ * Unexpected actions being performed when a key was pressed in
+ the memory and handle lists
+ * Changed I/O tray icon tooltip from ROW to RWO
+ * Corrupted usernames
+ * .NET processes getting recognized as packed
+ * Start times like "20 centuries ago"
+ * Unable to change service configurations
+ * "Access denied" when changing DEP status or unloading a module
+ on Windows XP
+
+1.3.7.5
+ * NEW/IMPROVED:
+ * #2780260 - "add key to open Proc Properties"
+ * #2780277 - "add to shortcut list for default action"
+ * #2781625 - "System Idle Process should not have network connections"
+ * #2784954 - "Ctrl+F find DLLs and not just Handles"
+ * Customizable tray icons - CPU History, CPU Usage, I/O History,
+ Commit History and Physical Memory History
+ * Base Priority, Start Time, and CPU Time columns
+ * "Terminate Process Tree"
+ * Can close TCP connections
+ * Process tree loads instantly
+ * Process properties (appears to) loads faster
+ * Decreased CPU usage
+ * Significantly less memory usage, especially when opening
+ process properties
+ * Thread termination now prompts
+ * Implemented Esc to close windows
+ * Cycles, Page Priority and I/O Priority in process statistics
+ * Integrity, I/O priority and page priority columns
+ * Windows are protected from being offscreen when they load
+ * Process property window locations are now saved
+ * The main window dimensions are saved when exiting minimized
+ * Enabled Reduce Working Set for multiple processes at a time
+ * Hides Process Hacker network connections by default
+ * FIXED:
+ * #2782808 - "Exception generated when CPU History is set to first column."
+ * #2784922 - "Hidden processes window: window location not remembered"
+ * #2784924 - "Network connections: process's icon not shown"
+ * Unhandled exception when process properties is closed within 100ms of
+ being opened
+ * Handle filter took a while to start up
+ * Forgot to add sorting for Private WS, Shared WS and Shareable WS
+ * dbghelp warnings were not being shown the first time process
+ properties opened
+ * Random file-object-related BSODs
+ * Increased PID limit in Hidden Processes to 65536
+ * Inaccurate I/O Total rates when using a refresh interval other than
+ 1000ms
+ * Incorrect thread start addresses
+
+1.3.7.1
+ * NEW:
+ * "-nokph" command line switch to disable KProcessHacker
+ * FIXED:
+ * #2779558 - "TreeViewAdv font cannot be initialized"
+ * KProcessHacker BSOD on some Vista systems
+ * Minor issue where new handle providers in the process window would
+ not be added to the shared thread provider
+
+1.3.7.0
+ * NEW/IMPROVED:
+ * Terminating processes and threads now bypasses all but the most
+ advanced anti-termination methods
+ * Better hidden processes scanner (similar to Blacklight's and IceSword's)
+ which can now detect both Hacker Defender and FU.
+ * Basic support for Windows 7 in Process Hacker and KProcessHacker
+ * Proper symbol support with dbghelp.dll
+ * Private, Shared and Shareable Working Set columns
+ * Improved handle viewing with KProcessHacker - more object types are visible,
+ including ALPC Ports and protected process handles
+ * Stack viewing uses KProcessHacker on Windows Vista
+ * Handle highlighting
+ * Lists now have column sorting priority
+ * Memory list is much faster
+ * Better thread start addresses, especially on Windows XP
+ * Job termination
+ * Elevation button in Options now spawns a child options window instead of
+ restarting Process Hacker elevated
+ * Can open process properties from the handle list
+ * Better "could not initialize configuration" message for Windows Vista
+ * New Terminator method: assigns a job object to the process and terminates it
+ * Process Properties menu item in the handle filter window
+ * Can now close multiple handles at once from the handle filter window
+ * FIXED:
+ * Service properties Key handle leak
+ * Handle deletion detection
+ * Unhandled exceptions when viewing performance/statistics for a non-existent process
+ * Network connections for processes without icons would not be displayed
+ * Virtualization menu item visible on Windows XP
+ * When processes are terminated they are deselected (to provide feedback to the user)
+ * When Native API calls failed they would pass through the exception handling
+ code unchecked, causing random crashes (rarely)
+ * REMOVED:
+ * Useless Window menu items (PITA + causes memory leaks due to Microsoft's poor
+ implementation of MenuItem)
+ * Registers from the thread window
+
+1.3.6.5
+ * NEW/IMPROVED:
+ * #2702907 - "CSR Processes: Enable termination of multiple processes"
+ * #2702909 - "CSR Processes: Show process name when confirming termination"
+ * #2702911 - "Show process name when terminating process in properties"
+ * #2702929 - "Add a Cancel button in Options"
+ * #2713088 - "Network: Ctrl+A and Ctrl+C should copy the processes' names"
+ * #2714130 - "Option to disable/enable all highlighting colors"
+ * Job information
+ * "Inject DLL" function
+ * Statistics times in System Information and process properties
+ * Highlighting system
+ * Configurable max. samples and plotter step size
+ * Network list with icons
+ * Can close multiple handles at once
+ * Less memory usage
+ * Confirmation dialogs are now consistent and use new Vista interfaces where possible
+ * KProcessHacker now retrieves thread start addresses
+ * KProcessHacker now performs memory manipulation (allowing the command lines of more
+ processes to be displayed)
+ * KProcessHacker now performs process suspending/resuming on Windows Vista
+ * Custom module information querying; can now display the modules for protected processes
+ * Displays service DLL paths
+ * Thread list displays cycles instead of context switches on Windows Vista
+ * GUI threads are highlighted (with KProcessHacker)
+ * Suspended and GUI thread highlighting can be configured
+ * Special tooltip information for dllhost.exe (shows COM target)
+ * FIXED:
+ * #2642442 - "System Information label text gets clipped"
+ * #2694437 - "Crash when sorting the process list"
+ * #2713087 - "Processes: Copy should copy only the columns currently used"
+ * #2716815 - "PH crashes during EnumProcesses"
+ * Network connections would be readded if their state changed
+ * Integer overflows in the process provider and system statistics
+ * Memory leaks with various windows
+ * Crash when saving with the I/O or CPU History column visible
+ * Inconsistent Copy menu after columns are modified
+ * Crash when F5 (Refresh) is held down
+ * Resizable statusbar
+ * Module unloading now works properly
+ * REMOVED:
+ * Disassembler - not used very often
+
+1.3.6.1
+ * NEW:
+ * CPU and I/O history columns
+ * System Cache value in System Information
+ * FIXED:
+ * #2625167 - "Commit charge limit should not be 16EB"
+ * #2642385 - "Maximum File Cache size should not be 16EB"
+ * Minimize size of the System Information window
+ * Settings were lost between versions
+ * Handle and memory leaks
+ * Integer overflows in System Information
+
+1.3.6.0
+ * NEW:
+ * #2596473 - "Add "Save Processes""
+ * #2596481 - "Add option for one instance of Process Hacker"
+ * #2601397 - "Ability to sort by name, type etc to all of the windows"
+ * #2605155 - "CSR Processes: Add Save Processes"
+ * #2625192 - "Log: Add clear log"
+ * #2625193 - "Add warning before shutting down, or restarting pc etc."
+ * #2647235 - "Update Log even when it’s open"
+ * #2647387 - "Add descriptions to Memory Editor's buttons"
+ * #2647418 - "Add ctrl+A and ctrl+C to threads, token, modules etc"
+ * #2647422 - "Suggest filename in Memory Editor->Data->Save"
+ * #2647435 - "Add descriptions to Memory search results window"
+ * #2657138 - "Add a toolbar"
+ * #2657143 - "Add Changelog in Help menu"
+ * #2675859 - "Log: auto-scroll option"
+ * #2675864 - "System Information improvements"
+ * #2675871 - "Suggest file extension everywhere"
+ * Full CPU, I/O and memory usage history for processes and the OS
+ * Tooltips for graphs
+ * Modules tab shows mapped files
+ * "Reduce Working Set" function
+ * Can change virtualization for processes
+ * Customizable columns for the process list/tree
+ * All lists now have Ctrl+A and Ctrl+C support
+ * Vista-style lists
+ * Changing service settings under a limited account now prompts for
+ elevation instead of giving an Access Denied error
+ * Run As now works under a limited account and prompts for elevation
+ * Shows environment variables
+ * Unicode string memory scanning
+ * Better Terminate Process confirmation dialog
+ * FIXED:
+ * #2602541 - "Bug with 'Hide When Minimized' enabled -svn635"
+ * Replace Task Manager works correctly when Start hidden is enabled
+ * csc.exe launching
+ * Black border bug when restoring from minimized state
+ * Nulls at the ends of handle names
+ * Errors when changing service settings
+ * Slow closing of various windows
+ * "Hide when minimized" is better
+ * Opened process properties when a plus/minus was double-clicked
+ * Redrawing of the process tree when the window is activated/deactivated
+ * Double-escaping in the Run As tool
+ * Priority getting/setting under limited accounts
+ * Run As tool sets new environment variables for the child process
+ * .NET processes are sometimes labelled as packed
+ * KProcessHacker BSOD
+
+1.3.5.0
+ * NEW:
+ * #2596502 - "Add access keys"
+ * #2596509 - "Double click on mutant-->Open properties window"
+ * #2596512 - "General tab window goes in the background"
+ * #2600995 - "Improvements to Help file"
+ * #2602538 - "Move some options to new window"
+ * #2602553 - "Add access key for Help"
+ * #2602606 - "Display a warning when running on 64bit WIN"
+ * #2605000 - "Add descriptions to some buttons"
+ * #2605133 - "Add access key to Log"
+ * #2605140 - "Save Log-->Suggest Log.txt"
+ * #2605146 - "When you open Log, the entries shouldn't be selected"
+ * #2605148 - "CSR Processes: Add select all and/or ctrl+A"
+ * #2605158 - "Log: Add Copy to clipboard and ctrl+A"
+ * #2605167 - "Add "Hide When Closed" option"
+ * #2608710 - "Notifications menu improvements"
+ * #2608801 - "Add Shutdown options"
+ * #2609039 - "Use thousands separator"
+ * #2613838 - "Add process' icon before process' name in process' window"
+ * #2615707 - "Improvements to Log"
+ * #2617637 - "Help file improvements No2"
+ * #2617691 - "Add option to replace task manager with Process Hacker"
+ * #2628961 - "Add ctrl+A in Services and Network window"
+ * #2628967 - "Add ctrl+C in Processes, Services and Network window"
+ * #2642505 - "Make some columns' size bigger"
+ * KProcessHacker is now enabled by default
+ * KProcessHacker now supports multiple clients at one time
+ * Process termination now uses KProcessHacker
+ * Process Hacker can now use KProcessHacker under a limited user account if it is
+ loaded, giving it full admin-like permissions to processes
+ * Process Hacker no longer requires UAC elevation - instead, there are two extra
+ menu items, "Run As Administrator..." and "Show Details for All Processes". It
+ also automatically prompts for elevation for specific tasks.
+ * "Run" function
+ * "Restart" function
+ * "Set Token" function for Windows XP
+ * "Verify File Signature" function
+ * "Re-analyze" function
+ * Around 50% reduction in CPU usage
+ * Specific highlighting can be enabled/disabled
+ * CSR Processes tool - displays hidden processes (this does not detect Hacker
+ Defender but does detect simple kernel-mode rootkits)
+ * Processes list in the notification icon menu, for quick actions on processes
+ * Options window has a detailed description for each setting
+ * Process window has descriptions for each field
+ * File processing (signature verification, checks for packed images) is now
+ asynchronous. This means that there are no more delays when starting
+ certain programs.
+ * Notification icon looks better and shows both user and kernel time
+ * GDI and USER handle counts
+ * Terminator's TT2 test is more effective now
+ * FIXED:
+ * Bug #2553406 - "COMException: Class not registered"
+ * Bug #2601383 - "Threads' column's width is not remembered."
+ * Bug #2612242 - "CPU usage > 100%"
+ * Bug #2615591 - "Bug with Show one graph per cpu?"
+ * Limited user account weirdness
+ * No longer deletes the KProcessHacker service if it's already started
+ * Memory searching now uses KProcessHacker if possible
+ * Incorrect labeling of process files which can't be read (due to permissions)
+ as packed
+ * Insane memory usage on Windows XP
+ * Now uses the proper method of detecting .NET processes
+ * System Information now runs on a separate thread
+ * Disables "Show one graph per CPU" in System Information if there is only one CPU
+ * Process tree weirdness for users with limited privileges
+ * CPU usage is now correctly displayed in graphs - K+U are not overlayed anymore
+ * "Require signatures for" names are now forced to be lower-case
+ * "Hide handles with no name" is now unique to each process window -
+ changing it in one window does not affect others
+ * Dialog/input boxes are now more consistent
+ * Very old bug where the refresh interval isn't applied at startup
+ * Displays-2-more-processes-than-actual-number bug
+ * System Information screwing up when the user tries to open it when it's
+ already open
+ * "a hour" -> "an hour", "yesterday ago" -> "a day ago"
+ * Handle leaks in the process window, thread window, handle list and process updater
+ * GDI+ handle leaks in the icon menu
+ * PH window is permanently offscreen if it is hidden when minimized
+ * Process colors not being refreshed when items are removed
+
+1.3.2.0
+ * NEW:
+ * Network tab - shows current network connections
+ * KProcessHacker - an experimental kernel-mode driver for Process Hacker which
+ finally enables Process Hacker to display all file handles without freezing
+ * Can protect and unprotect processes (Vista's DRM protection)
+ * System Information menu item in the tray icon menu
+ * Hide when minimized option
+ * Now resolves device names into drive letters - e.g. "\Device\Harddisk1\FileName" into
+ "C:\FileName".
+ * Properly verifies system components instead of just checking file permissions
+ * Configurable dangerous-process-names highlighting
+ * Added protection against PEB file name spoofing
+ * Customizable fonts
+ * Command line switch: -m to hide Process Hacker
+ * Nice relative times - "2 seconds ago", "14 minutes and 33 seconds ago", etc.
+ * Displays service descriptions, dependencies, and dependents
+ * Offers to reset settings if they are corrupt
+ * FIXED:
+ * Bug #2527154 - "Process Hacker crashes on Win XP"
+ * Small UI enabling/disabling fixes
+ * Now the process tree and service list update instantly after
+ starting Process Hacker instead of waiting
+ * Random "Generic GDI+ Error" exceptions
+ * Small performance improvements
+ * Now shows proper command lines of programs where they contain null characters
+ * Handle filter actually works now
+ * Relative RunDLL targets
+ * Handle leaks with threads
+
+1.3.1.0
+ * NEW:
+ * "Free" and "Decommit" actions for memory regions
+ * "Description" column for processes
+ * Current Directory for processes automatically updates
+ * Can now display the file names of DRM-protected
+ processes (like audiodg.exe)
+ * Now displays thread information under the thread list
+ * Module file name info for thread start addresses
+ and stack traces
+ * Highlighting for .NET processes and packed executables
+ * Shows CPU usage and physical memory usage in the status bar
+ * "Reload Struct Definitions" menu item
+ * Struct Searcher - displays addresses which match the specified
+ struct definition
+ * Ability to unload remote modules (by remote thread
+ injection)
+ * New float and double types for structs
+ * Special tooltip info for rundll32.exe
+ * Verifies file signatures (and detects Windows components by
+ checking the files' owners and ACLs)
+ * Highlights processes which have invalid signatures or are
+ pretending to be system processes
+ * Better method for suspending/resuming processes
+ * FIXED:
+ * System information window resizing
+ * "Overflow error" exceptions
+ * Problems with the search button
+ * Process properties for DPCs and Interrupts
+ * Disabled expanding of processes when double-clicking
+ them
+ * Now shows non-existent parent PIDs
+ * build-and-clean script is now XP compatible
+ * Redrawing problems with the lists in the process window
+
+1.3.0.0
+ * NEW:
+ * Process tree using TreeViewAdv
+ * Process properties window with statistics and graphs
+ * System Information Window with statistics and graphs
+ * Detailed token information, including source, owner and primary group
+ * Information about remote handles to events, mutants, sections and tokens
+ * Terminator tool - tries many techniques to terminate processes
+ * Highlighting for UAC elevated processes and processes in job objects
+ * Struct reader for examining PEBs and TEBs - note that this can be
+ extended by writing your own definitions
+ * Better tray icon - displays a graph, and the tooltip contains the current
+ CPU usage and the process using most of the CPU
+ * FIXED:
+ * Shows SIDs without names (like Logon IDs) in SDDL format
+ * Uses PROCESS_QUERY_LIMITED_INFORMATION on Windows Vista - e.g. on audiodg.exe
+ * Symbols are now bound to each process - no more weird stack traces/symbols
+ * Handle filtering is now much faster - uses a cache for session ID checking
+
+1.2.6.5
+ * A new member of the project - Dean
+ * Can view thread usernames, groups and privileges
+ * The handle filter window is now faster
+ * Added thread start addresses in thread list
+ * The process list now has less CPU usage
+ * Fixed some processes not having icons
+
+1.2.6.0
+ * Fixed the fix for the huge regression - the cause was a double "free" of the same handle
+ * Added Assistant - can start processes as any user, including SYSTEM,
+ LOCAL SERVICE and NETWORK SERVICE. Injector's create process item is
+ now deprecated.
+ * Fixed service handle leaks which caused service deletions to be
+ undetected.
+ * Fixed service list in process tooltips when new own-process services are started and when
+ shared-process services are stopped.
+
+1.2.5.1
+ * Fixed huge regression caused by revision #250 in Win32.ProcessHandle.~ProcessHandle()
+
+1.2.5.0
+ * Fixed wrong usernames when Process Hacker is running as a non-admin user
+ * Gets command line of processes without using Injector
+ * Added highlighting for debugged processes
+ * Added viewing/setting of process affinity
+ * Fixed all handle leaks
+ * Added I/O counters to misc. info
+ * Added CPU usage column
+ * Fixed services in tooltips
+ * Added highlighting for processes with services
+ * System and System Idle Processes now have usernames (hardcoded in)
+ * Handle finder now only searches in processes with same session ID (faster,
+ avoids hangs)
+ * Fixed window activation when showing Process Hacker from the tray icon
+ * Fixed highlighting
+ * Fixed various memory corruption issues
+
+1.2.3.5
+ * Added handle viewing/closing support
+ * Fixed random crashes
+ * Doesn't update threads/handles unless viewing them
+ * Fixed most handle leaks
+ * Fixed randomly changing process usernames
+ * Added WaitReason column in thread list
+ * Highlights suspended threads in thread list
+ * Only require highestAvailable elevation for UAC
+
+1.2.3.0
+ * Fixed incorrect messages about "new services" (actually fixed this time)
+ * Fixed Injector - now uses MinGW instead of Visual C++
+ * "Create Process..." feature of Injector works on XP now
+ * Added StartType column in services list
+
+1.2.2.5
+ * Now shows function parameters in call stack
+ * Added Injector
+ * Added "Get Symbol Name From Address..." and "Find SYSTEM processes with same Session ID"
+ * Services list now displays a blank space when there is no PID
+ * Fixed services with own process and interactive type
+ * Fixed random crashes with the process list and services list
+ * Added copyright information (to comply with VistaMenu and SplitButton licensing)
+ * Now displays user names of most processes using the Terminal Server APIs
+ * Fixed incorrect messages about "new services"
+ * Added tray icon
+
+1.2.1.5
+ * Fixed disassemblies from PE window - they disassemble the right function now
+ * Fixed UI inconsistencies
+ * Allow Toolhelp module listings
+ * Updated help
+ * Fixed service start/stop buttons in service window
+
+1.2.1.0
+ * Can now display services
+ * Displays services in process tooltips
+
+1.2.0.5
+ * Updated credits
+ * Fixed crash when opening two thread inspectors with the same process and thread
+ * Disassembler window can now display backward short jumps (>0x7fffffff)
+ * Now unloads symbols for all other EXEs before opening the thread inspector
+ * Improvements in symbol loading
+
+1.2.0.0
+ * Added PE Inspector (can read exe/dll/sys files and their properties)
+ * Added "Search Online" menu item
+ * Added highlighting of processes and threads
+ * Added "Always on Top" menu item
+ * Added status bar messages and log
+ * Added "Go to Parent" menu item
+ * Removed "Close Active Window" menu item - practically same as "Terminate Process"
+ * Added disassembler
+ * Added Thread Inspector - shows call stack of threads
+
+1.1.5.0
+ * Fixed memory leak with process icons
+ * Removed useless starting "\" in account names
+ * Fixed group menu item enabling and disabling
+ * Added filtering of search results
+
+1.1.4.5
+ * Fixed crash when attempting to open privileges for System on XP
+ * Added Groups Window (to view process groups)
+ * Fixed Search window startup position
+
+1.1.4.0
+ * Enabled Select All for all lists on the main window
+ * Added Privileges Window (to enable, disable and remove privileges)
+ * Fixed error messages - they now have the correct Win32 error descriptions
+
+1.1.3.5
+ * Now saves column settings
+ * Now saves results and memory window settings
+ * Now saves selected tab
+
+1.1.3.0
+ * Completely rewrote thread list code
+ * Can now display kernel threads on Vista
+ * Disables thread priority menu item when there is an error
+ * Now displays process 0 as System Idle Process
+ * Fixed thread list for System Idle Process
+ * Now displays description for System
+ * Now has a good system of copying list items (currently in every ListView)
+
+1.1.2.0
+ * Completely rewrote process list code
+ * Added lots of information to process tooltips
+
+1.1.1.0
+ * Memory editor is now properly activated from the Hacker Window
+ * Fixed obscure bug where the process list disables itself
+ * Added my email address
+ * Added CONTEXT code
+ * Now bolds the kernel name when the System process is selected
+ * Fixed opening Memory Editor from Results Window
+ * Changed Search button to a SplitButton
+ * Added username column
+ * Now shows process description and filename in tooltip
+ * Added more "options"
+ * Added keyboard shortcuts for terminating process(es) (Del) and refreshing (F5)
+ * Added some menu items - Select All and Refresh
+
+1.1.0.5
+ * Upgraded messageboxes when performing operations on processes and threads to OKCancel
+ * Fixed checking for dangerous PIDs on XP
+ * Fixed getting icons for certain processes on XP
+ * Fixed crashes when attempting to read from invalid memory locations
+ * Fixed crash when opening help window after closing it
+ * Fixed default menu item text drawing
+
+1.1.0.0
+ * Fixed certain programs' modules not having a description
+ * Major refactoring in code
+ * Moved memory editor panel into its own form
+ * Moved search into its own form
+ * Merged string scanning and heap scanning into the Search form
+ * Removed debug programs option
+ * Now has proper main menu
+ * Search results window is now on its own thread
+ * Search results list now uses VirtualMode - huge speed increase
+ * Added "prevent overlapping results" option
+ * Made memory editor windows spawn in separate threads
+ * Removed all Application.DoEvents() calls
+ * Fixed cursor problems
+ * Now displays busy cursor on startup
+ * Intersect menu is now sorted
+ * Fixed form focusing
+ * Added Window menu items in all forms
+
+1.0.1.0
+ * Fixed occasional "Object not set to an instance of object" error
+ * Added link to SourceForge project page
+ * Fixed FlatStyle inconsistencies
+ * Fixed "invalid characters in path name" problem on Windows X
\ No newline at end of file
diff --git a/branches/ph-plugins/ExtraTools/ExtraTools.sln b/branches/ph-plugins/ExtraTools/ExtraTools.sln
new file mode 100644
index 000000000..bc820629f
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/ExtraTools.sln
@@ -0,0 +1,50 @@
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProcessHacker.Native", "..\ProcessHacker.Native\ProcessHacker.Native.csproj", "{8A448157-E1A7-4DDF-954E-287F1117832B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProcessHacker.Common", "..\ProcessHacker.Common\ProcessHacker.Common.csproj", "{8E10F5E8-D4FA-4980-BB23-2EDD134AC15E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NtObjects", "NtObjects\NtObjects.csproj", "{06AC6477-D3DA-4997-9A4A-4F809F7C9396}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NtProfiler", "NtProfiler\NtProfiler.csproj", "{E3CEB6D7-7080-4089-B54E-41025E30CE46}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProcessAnalyzer", "ProcessAnalyzer\ProcessAnalyzer.csproj", "{A6709B97-F7B5-40AD-AB6E-F23019BA0A3C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SysCallHacker", "SysCallHacker\SysCallHacker.csproj", "{39B5CDC9-0AB3-4E1F-862C-EA95BC5A0715}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {8A448157-E1A7-4DDF-954E-287F1117832B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8A448157-E1A7-4DDF-954E-287F1117832B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8A448157-E1A7-4DDF-954E-287F1117832B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8A448157-E1A7-4DDF-954E-287F1117832B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8E10F5E8-D4FA-4980-BB23-2EDD134AC15E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8E10F5E8-D4FA-4980-BB23-2EDD134AC15E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8E10F5E8-D4FA-4980-BB23-2EDD134AC15E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8E10F5E8-D4FA-4980-BB23-2EDD134AC15E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {06AC6477-D3DA-4997-9A4A-4F809F7C9396}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {06AC6477-D3DA-4997-9A4A-4F809F7C9396}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {06AC6477-D3DA-4997-9A4A-4F809F7C9396}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {06AC6477-D3DA-4997-9A4A-4F809F7C9396}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E3CEB6D7-7080-4089-B54E-41025E30CE46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E3CEB6D7-7080-4089-B54E-41025E30CE46}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E3CEB6D7-7080-4089-B54E-41025E30CE46}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E3CEB6D7-7080-4089-B54E-41025E30CE46}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A6709B97-F7B5-40AD-AB6E-F23019BA0A3C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A6709B97-F7B5-40AD-AB6E-F23019BA0A3C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A6709B97-F7B5-40AD-AB6E-F23019BA0A3C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A6709B97-F7B5-40AD-AB6E-F23019BA0A3C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {39B5CDC9-0AB3-4E1F-862C-EA95BC5A0715}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {39B5CDC9-0AB3-4E1F-862C-EA95BC5A0715}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {39B5CDC9-0AB3-4E1F-862C-EA95BC5A0715}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {39B5CDC9-0AB3-4E1F-862C-EA95BC5A0715}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/branches/ph-plugins/ExtraTools/NtObjects/NtObjects.csproj b/branches/ph-plugins/ExtraTools/NtObjects/NtObjects.csproj
new file mode 100644
index 000000000..80fb1ef7a
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/NtObjects/NtObjects.csproj
@@ -0,0 +1,92 @@
+
+
+
+ Debug
+ AnyCPU
+ 9.0.30729
+ 2.0
+ {06AC6477-D3DA-4997-9A4A-4F809F7C9396}
+ WinExe
+ Properties
+ NtObjects
+ NtObjects
+ v2.0
+ 512
+
+
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ AnyCPU
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ AnyCPU
+
+
+
+
+
+
+
+
+ Form
+
+
+ ObjectsWindow.cs
+
+
+
+
+ ObjectsWindow.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+ Designer
+
+
+ True
+ Resources.resx
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+ True
+ Settings.settings
+ True
+
+
+
+
+ {8E10F5E8-D4FA-4980-BB23-2EDD134AC15E}
+ ProcessHacker.Common
+
+
+ {8A448157-E1A7-4DDF-954E-287F1117832B}
+ ProcessHacker.Native
+
+
+
+
+
\ No newline at end of file
diff --git a/branches/ph-plugins/ExtraTools/NtObjects/ObjectsWindow.Designer.cs b/branches/ph-plugins/ExtraTools/NtObjects/ObjectsWindow.Designer.cs
new file mode 100644
index 000000000..4399d9a38
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/NtObjects/ObjectsWindow.Designer.cs
@@ -0,0 +1,163 @@
+namespace NtObjects
+{
+ partial class ObjectsWindow
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.components = new System.ComponentModel.Container();
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ObjectsWindow));
+ this.splitContainer = new System.Windows.Forms.SplitContainer();
+ this.treeDirectories = new System.Windows.Forms.TreeView();
+ this.imageList = new System.Windows.Forms.ImageList(this.components);
+ this.listObjects = new System.Windows.Forms.ListView();
+ this.columnName = new System.Windows.Forms.ColumnHeader();
+ this.columnType = new System.Windows.Forms.ColumnHeader();
+ this.columnData = new System.Windows.Forms.ColumnHeader();
+ this.splitContainer.Panel1.SuspendLayout();
+ this.splitContainer.Panel2.SuspendLayout();
+ this.splitContainer.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // splitContainer
+ //
+ this.splitContainer.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.splitContainer.Location = new System.Drawing.Point(0, 0);
+ this.splitContainer.Name = "splitContainer";
+ //
+ // splitContainer.Panel1
+ //
+ this.splitContainer.Panel1.Controls.Add(this.treeDirectories);
+ //
+ // splitContainer.Panel2
+ //
+ this.splitContainer.Panel2.Controls.Add(this.listObjects);
+ this.splitContainer.Size = new System.Drawing.Size(794, 441);
+ this.splitContainer.SplitterDistance = 264;
+ this.splitContainer.TabIndex = 0;
+ //
+ // treeDirectories
+ //
+ this.treeDirectories.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.treeDirectories.HideSelection = false;
+ this.treeDirectories.ImageKey = "directory";
+ this.treeDirectories.ImageList = this.imageList;
+ this.treeDirectories.Location = new System.Drawing.Point(0, 0);
+ this.treeDirectories.Name = "treeDirectories";
+ this.treeDirectories.SelectedImageKey = "directory";
+ this.treeDirectories.Size = new System.Drawing.Size(264, 441);
+ this.treeDirectories.TabIndex = 0;
+ this.treeDirectories.MouseDown += new System.Windows.Forms.MouseEventHandler(this.treeDirectories_MouseDown);
+ this.treeDirectories.NodeMouseClick += new System.Windows.Forms.TreeNodeMouseClickEventHandler(this.treeDirectories_NodeMouseClick);
+ //
+ // imageList
+ //
+ this.imageList.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("imageList.ImageStream")));
+ this.imageList.TransparentColor = System.Drawing.Color.Transparent;
+ this.imageList.Images.SetKeyName(0, "object");
+ this.imageList.Images.SetKeyName(1, "directory");
+ this.imageList.Images.SetKeyName(2, "symboliclink");
+ this.imageList.Images.SetKeyName(3, "event");
+ this.imageList.Images.SetKeyName(4, "mutant");
+ this.imageList.Images.SetKeyName(5, "device");
+ this.imageList.Images.SetKeyName(6, "key");
+ this.imageList.Images.SetKeyName(7, "alpc port");
+ this.imageList.Images.SetKeyName(8, "port");
+ this.imageList.Images.SetKeyName(9, "section");
+ this.imageList.Images.SetKeyName(10, "job");
+ this.imageList.Images.SetKeyName(11, "callback");
+ this.imageList.Images.SetKeyName(12, "type");
+ this.imageList.Images.SetKeyName(13, "windowstation");
+ this.imageList.Images.SetKeyName(14, "desktop");
+ this.imageList.Images.SetKeyName(15, "filterconnectionport");
+ this.imageList.Images.SetKeyName(16, "semaphore");
+ this.imageList.Images.SetKeyName(17, "session");
+ this.imageList.Images.SetKeyName(18, "keyedevent");
+ this.imageList.Images.SetKeyName(19, "driver");
+ //
+ // listObjects
+ //
+ this.listObjects.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
+ this.columnName,
+ this.columnType,
+ this.columnData});
+ this.listObjects.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.listObjects.FullRowSelect = true;
+ this.listObjects.HideSelection = false;
+ this.listObjects.Location = new System.Drawing.Point(0, 0);
+ this.listObjects.Name = "listObjects";
+ this.listObjects.Size = new System.Drawing.Size(526, 441);
+ this.listObjects.SmallImageList = this.imageList;
+ this.listObjects.Sorting = System.Windows.Forms.SortOrder.Ascending;
+ this.listObjects.TabIndex = 0;
+ this.listObjects.UseCompatibleStateImageBehavior = false;
+ this.listObjects.View = System.Windows.Forms.View.Details;
+ this.listObjects.DoubleClick += new System.EventHandler(this.listObjects_DoubleClick);
+ //
+ // columnName
+ //
+ this.columnName.Text = "Name";
+ this.columnName.Width = 200;
+ //
+ // columnType
+ //
+ this.columnType.Text = "Type";
+ this.columnType.Width = 100;
+ //
+ // columnData
+ //
+ this.columnData.Text = "Data";
+ this.columnData.Width = 200;
+ //
+ // ObjectsWindow
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(794, 441);
+ this.Controls.Add(this.splitContainer);
+ this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
+ this.Name = "ObjectsWindow";
+ this.Text = "NtObjects";
+ this.splitContainer.Panel1.ResumeLayout(false);
+ this.splitContainer.Panel2.ResumeLayout(false);
+ this.splitContainer.ResumeLayout(false);
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.SplitContainer splitContainer;
+ private System.Windows.Forms.TreeView treeDirectories;
+ private System.Windows.Forms.ImageList imageList;
+ private System.Windows.Forms.ListView listObjects;
+ private System.Windows.Forms.ColumnHeader columnName;
+ private System.Windows.Forms.ColumnHeader columnType;
+ private System.Windows.Forms.ColumnHeader columnData;
+
+ }
+}
+
diff --git a/branches/ph-plugins/ExtraTools/NtObjects/ObjectsWindow.cs b/branches/ph-plugins/ExtraTools/NtObjects/ObjectsWindow.cs
new file mode 100644
index 000000000..23ae7eeb7
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/NtObjects/ObjectsWindow.cs
@@ -0,0 +1,174 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Text;
+using System.Windows.Forms;
+using ProcessHacker.Native;
+using ProcessHacker.Native.Api;
+using ProcessHacker.Native.Objects;
+using ProcessHacker.Native.Security;
+
+namespace NtObjects
+{
+ public partial class ObjectsWindow : Form
+ {
+ private class TreeViewSorter : System.Collections.IComparer
+ {
+ public int Compare(object x, object y)
+ {
+ return ((TreeNode)x).Text.CompareTo(((TreeNode)y).Text);
+ }
+ }
+
+ public ObjectsWindow()
+ {
+ InitializeComponent();
+
+ try
+ {
+ using (var thandle = ProcessHandle.GetCurrent().GetToken(TokenAccess.AdjustPrivileges))
+ {
+ try { thandle.SetPrivilege("SeCreateGlobalPrivilege", SePrivilegeAttributes.Enabled); }
+ catch { }
+ }
+ }
+ catch
+ { }
+
+ treeDirectories.TreeViewNodeSorter = new TreeViewSorter();
+ Win32.SetWindowTheme(treeDirectories.Handle, "explorer", null);
+ treeDirectories.Nodes.Add("\\", "\\");
+ treeDirectories.SelectedNode = treeDirectories.Nodes["\\"];
+ this.PopulateDirectories();
+ this.ChangeDirectory();
+ treeDirectories.SelectedNode.Expand();
+ }
+
+ private void treeDirectories_MouseDown(object sender, MouseEventArgs e)
+ {
+ treeDirectories.SelectedNode = treeDirectories.GetNodeAt(e.Location);
+ }
+
+ private void PopulateDirectories()
+ {
+ this.PopulateDirectory("\\");
+ }
+
+ private void PopulateDirectory(string directory)
+ {
+ try
+ {
+ using (DirectoryHandle dhandle =
+ new DirectoryHandle(directory, DirectoryAccess.Query))
+ {
+ var objects = dhandle.GetObjects();
+
+ foreach (var obj in objects)
+ {
+ if (obj.TypeName != "Directory")
+ continue;
+
+ this.GetTreeNode(directory).Nodes.Add(obj.Name, obj.Name);
+
+ this.PopulateDirectory(this.NormalizePath(directory + "\\" + obj.Name));
+ }
+ }
+ }
+ catch (WindowsException)
+ { }
+ }
+
+ private string NormalizePath(string path)
+ {
+ string[] s = path.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);
+
+ return "\\" + string.Join("\\", s);
+ }
+
+ private TreeNode GetTreeNode(string path)
+ {
+ return this.GetTreeNode(treeDirectories.Nodes["\\"], path, 0);
+ }
+
+ private TreeNode GetTreeNode(TreeNode root, string path, int index)
+ {
+ string[] s = path.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);
+
+ if (index >= s.Length)
+ return root;
+
+ if (root.Nodes[s[index]] == null)
+ return null;
+
+ return this.GetTreeNode(root.Nodes[s[index]], path, index + 1);
+ }
+
+ private void ChangeDirectory()
+ {
+ listObjects.Items.Clear();
+
+ if (treeDirectories.SelectedNode != null)
+ {
+ listObjects.BeginUpdate();
+
+ try
+ {
+ using (DirectoryHandle dhandle =
+ new DirectoryHandle(this.NormalizePath(treeDirectories.SelectedNode.FullPath), DirectoryAccess.Query))
+ {
+ var objects = dhandle.GetObjects();
+
+ foreach (var obj in objects)
+ {
+ var item = listObjects.Items.Add(new ListViewItem(new string[] { obj.Name, obj.TypeName, "" }));
+
+ if (imageList.Images.ContainsKey(obj.TypeName.ToLower()))
+ item.ImageKey = obj.TypeName.ToLower();
+ else
+ item.ImageKey = "object";
+
+ if (obj.TypeName == "SymbolicLink")
+ {
+ try
+ {
+ using (SymbolicLinkHandle shandle =
+ new SymbolicLinkHandle(
+ this.NormalizePath(
+ treeDirectories.SelectedNode.FullPath +
+ "\\" + obj.Name),
+ SymbolicLinkAccess.Query))
+ item.SubItems[2].Text = shandle.GetTarget();
+ }
+ catch
+ { }
+ }
+ }
+ }
+ }
+ catch (WindowsException)
+ { }
+
+ listObjects.EndUpdate();
+ }
+ }
+
+ private void treeDirectories_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
+ {
+ this.ChangeDirectory();
+ }
+
+ private void listObjects_DoubleClick(object sender, EventArgs e)
+ {
+ if (listObjects.SelectedItems.Count != 1)
+ return;
+
+ if (listObjects.SelectedItems[0].SubItems[1].Text == "Directory")
+ {
+ treeDirectories.SelectedNode =
+ treeDirectories.SelectedNode.Nodes[listObjects.SelectedItems[0].SubItems[0].Text];
+ this.ChangeDirectory();
+ }
+ }
+ }
+}
diff --git a/branches/ph-plugins/ExtraTools/NtObjects/ObjectsWindow.resx b/branches/ph-plugins/ExtraTools/NtObjects/ObjectsWindow.resx
new file mode 100644
index 000000000..f35146b97
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/NtObjects/ObjectsWindow.resx
@@ -0,0 +1,1299 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 17, 17
+
+
+
+ AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj0yLjAuMC4w
+ LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0
+ ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAADo
+ WAAAAk1TRnQBSQFMAgEBFAEAARwBAAEEAQABEAEAARABAAT/ASEBAAj/AUIBTQE2BwABNgMAASgDAAFA
+ AwABYAMAAQEBAAEgBgABYP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8APgADAgEDATsBTQFZ
+ AXwBKAFhAY8B6QEmAV4BjAHsATgBWAF3AbsBFwEYARkBIGQAAUMBWwFrAYUBSQGVAcIB6QFDAYQBswHg
+ ATMBOwFCAVYcAAE9AXoBzwHVASgBgQL/ASABfAL/AToBZgGlAa9EAAE8AVIBXwGCAR0BcAGnAf8BKgGL
+ Ab0B/wEzAaMB2QH/ATIBnwHZAf8BJgF4AbMB/wE7AUwBWwGCFAADCgENAw8BEwFyAU8BPQGYAZMBRgEt
+ AeMBpAFGAScB9QGpAUcBJAH6AagBRQEkAfoBogFDASUB9gGVAUIBKQHqAYgBUwE8AboDEQEWAwoBDRgA
+ AwoBDQMmATEBUAGUAbYB0wGGAekB+QH/AUwB2QH1Af8BPQGOAcIB8wE1AT0BRQFdFAABSQF+AcQBzAFg
+ AaQC/wGXAcIC/wGUAcAC/wFQAZgC/wE7AWYBpQGvOAABLAEzATcBRwEeAXgBrQH/AS8BmAHGAf8BSgHI
+ AeMB/wFGAcUB5wH/ASgBnQHIAf8BJwG2AeQB/wEtAZcB0AH/ATUBYAGDAckQAAFNAVkBZAF5ASYBdwHJ
+ AfsBhQJgAfsBvwFfATQB/wH+AbkBYAH/Af4BuQFhAf8B/gG5AWEB/wH+AbkBYQH/Af4BuQFgAf8B/gG5
+ AWAB/wGxAUgBIwH/AXcBYwFsAfUBMQF9AcgB+AFRAV8BbAGDCAADGgEhA28BnQOYAfADoQH/A6sB/wFZ
+ AaoB1gH/AaEB5gH4Af8BNwHSAfIB/wFGAdYB9gH/AT0BkQHFAfYBNwE/AUgBYgMKAQ0DCgENCAABSAGV
+ Av8BoAHHAv8BgwG3Av8BfgG0Av8BlwHCAv8BUgGZAv8BPAFnAaUBrzAAATwBaQGAAa4BLgGOAb0B/wE3
+ AYwBugH/ASoBdwGvAf8BQQGXAcYB/wELAVoBlQH/AQgBSQGIAf8BGwGSAc4B/wEzAacB3QH/ASwBZgGS
+ AeEQAAEpAX0B0AH+AYIBugHuAf8BnwFlAVcB/wH1AbsBhAL/AawBWgH/Af4BqAFZAf8B/gGiAVYB/wH+
+ AZwBUgL/AaMBVAL/AZ8BTwH/AfgBrgF3Af8BpAFdAUkB/wGDAbwB7wH/ASoBdwHJAf4EAAM5AUoDoAH0
+ A84B/wPtAf8D9AH/A/UB/wGtAdMB6wH/AVwBrgHgAf8BmQHiAfYB/wFSAdwB9QH/AUUB2QH2Af8BOgGN
+ AcQB9gE8AX0BvAHoAT0BfgG/AegBMQE4AT8BUwQAAVIBnQL/AaQBywL/AYsBvAL/AXYBsAL/AYABtQL/
+ AZgBwwL/AVUBnAL/AT4BZwGiAawoAAEyAXsBoQHZATABoAHIAf8BVgGeAcgB/wFyAb0B3gH/AYIB3gH2
+ Af8BWAG0AdoB/wEMAVwBkQH/ARUBgAG9Af8BJAGyAfMB/wE8Aa4B4gH/ATIBbAGRAdUQAAEpAXsBywH8
+ AXcBswHqAf8BswGeAZQC/wG3AV8C/wG2AWIB/wH+AbIBYAH/Af4BrAFcAf8B/gGlAVgB/wH9AZ4BUgH/
+ Af4BlwFNAv8BjQFCAf8BvAGPAYIB/wF9AbgB7QH/ASoBcwHDAfoEAAOeAeoD3gH/A/MB/wPbAf8D0gH/
+ A9sB/wPWAf8BogG3AcUB/wFgAbAB3AH/AWIBvgHlAf8BXQHZAfIB/wFNAdsB9gH/AVoB3QH3Af8BVAHY
+ AfUB/wEyAX8BxwH3ATMBOgFDAVkBUQFoAYcBkgF4AbMC/wGlAcwC/wGNAb0C/wF4AbIC/wGDAbYC/wGZ
+ AcMC/wFXAZwC/wE/AWgBogGsIAABOAF6AZkBxgExAacBzwH/AXkB3QHyAf8BLQF2Aa8B/wGMAeAB9gH/
+ AXcB0AHtAf8BKwGKAbgB/wEdAY0ByQH/ASUBtQHvAf8BHwGdAd0B/wFDAbUB5QH/ASkBegGsAe4QAAE9
+ ATUBMQFQAYoBUwFDAf8B/AHIAasC/wHRAZgB/wH+AccBbAH/Af4BvwFnAf8B/gG5AWMB/wH+AbEBXQH/
+ Af4BqAFYAf8B/QGgAVMC/wG3AXkB/wH+AakBgAH/AYgBTwFBAf8BRQE8ATcBYAQAA6AB6gPwAf8D3gH/
+ A9QB/wPSAf8D2wH/A9YB/wO/Af8DsAH/AW8BpQHHAf8BiQHdAfQB/wFpAeAB9gH/AXIB4gH3Af8BXgHf
+ AfYB/wFUAdoB9gH/ATsBfQHDAfMEAAFSAWkBhwGSAXoBtAL/AaYBzAL/AY4BvgL/AXsBswL/AYUBuAL/
+ AZsBxQL/AVgBngL/AUABaAGgAaoYAAE/AWgBewGaATsBtAHaAf8BewHZAe4B/wFOAa4B1AH/ATcBgQG0
+ Af8BPwGSAb0B/wEgAYsBvAH/AS0BrAHfAf8BLwG5Ae4B/wEgAZIB0gH/AR4BnQHdAf8BQwG3AegB/wEq
+ AZYBzwH/ASwBMwE3AUcPAAEBAUkBOwEzAWABxAFLAR4B/wH2AeQB1gL/AeQBpAL/AdQBcQL/AckBaAL/
+ AcABYgL/AbYBXgL/AcEBgAH/AfYB1wHGAf8BxQFIAR4B/wFNAT0BNQFpAwMBBAQAA6IB6gPyAf8D4gH/
+ A9gB/wPVAf8D3AH/A9gB/wPAAf8DswH/AWEBqAHRAf8BqQHuAfkB/wF9AeYB+AH/AZoB6AH4Af8BfQHR
+ AfAB/wGAAeIB9gH/AUIBkgHLAfAIAAFTAWkBhwGSAXsBtgL/AagBzgL/AZABvwL/AYsBvQL/AaAByAL/
+ AWABpAL/AUUBdwG+AckYAAEwAZUBvQHlAWgB3QHyAf8BggHdAfIB/wErAaQBygH/ASkBqgHQAf8BNQGp
+ AdUB/wE1AakB3AH/ATUBuAHpAf8BIwGRAcsB/wEiAZABzgH/ASMBpgHjAf8BRAG6AesB/wE7AawB4QH/
+ ATwBYwF7AawQAAMFAQcBTwE/ATYBaQG8AUcBGwH/AfQB4gHUAf8BTQF6AakB/wFMAXoBqAH/AUwBegGo
+ Af8BTQF6AakB/wHzAdYBwwH/Ab4BRQEbAf8BUQFAATcBbwMIAQoIAAOkAeoD8wH/A+cB/wPdAf8D2QH/
+ A+AB/wPbAf8DxAH/A7gB/wGDAawBxwH/AV0BwQHqAf8BowHwAfsB/wGAAdQB8AH/AX0BxwHsAf8BUQGe
+ AdMB9QE0AT0BRgFZDAABVAFqAYcBkgF9AbcC/wGpAc4C/wGoAc0C/wFvAawC/wFeAZoB7gH/A4cB9wMb
+ ASMUAAE8AYcBnQG8AVQB0gHrAf8BnQHoAfkB/wF3AeEB9gH/AWAB2AH2Af8BUQHTAfcB/wFAAcEB6gH/
+ ATABqgHbAf8BKgGfAdYB/wElAZcB0QH/ASYBpwHgAf8BKgGvAekB/wFFAbYB5gH/AS0BdQGfAeIUAAME
+ AQUBWwFIAT8BogEzAWwBpwH/AZwBzAH4Af8BrwHUAfcB/wGvAdQB9wH/AaUBzwH2Af8BMwFzAa4B/wFe
+ AUoBQgGtAwcBCQwAA6UB6gP0Af8D6gH/A+EB/wPdAf8D4wH/A94B/wPJAf8DvQH/A78B/wGRAb4B3gH/
+ AW4ByQHsAf8ByQHzAfsB/wFYAa4B1QHtAS0BNQE7AUgUAAFUAWsBhwGSAYEBtwL/AXsBtAL/AW4BpwHy
+ Af8D0gH/A7UB/wOIAf4DhgH7A4QB+wN5AdYDTQFwBAABGwEdAR4BJQExAagBxgHmASYBsgHZAf4BfQHT
+ AesB/wHLAfAB+wH/AYQB3QH1Af8BRgHGAesB/wE9Ab0B6QH/ATYBtQHlAf8BLAGmAdwB/wErAawB4wH/
+ ASoBsAHpAf8BRwG5AegB/wEkAYQBuwH5GAABPgFbAXwBxAGmAcoB7gH/AasBzAHqAf8BpwHQAfYB/wGo
+ AdAB9gH/AasBzAHqAf8BpwHNAe4B/wE+AV8BhAHMEAADpwHqA/UB/wPuAf8D5gH/A+IB/wPmAf8D4QH/
+ A80B/wPCAf8DwgH/AuIB4wH/AWEBmAG5AfUBTgGOAbABywE4AUgBUwFjHAABVgFrAYcBkgFkAY0BwgHP
+ A5EB+wPHAf8DzAH/A8cB/wPGAf8DwwH/A8AB/wOCAfUDTQFwCAABEwIUARkBPQGLAZ8BuwFXAcsB5gH/
+ AakB4AHzAf8BogHjAfgB/wFJAcsB8QH/AUABxAHvAf8BOQG9AewB/wEyAbgB6wH/AS0BtAHqAf8BSAG8
+ AeoB/wElAY0ByAH7GAABKAFeAZQB7QHZAegB9wH/AZcBxQHxAf8BjgG7AeUB/wF+AakB0QH/AYkBtQHf
+ Af8BzQHfAe4B/wEqAWcBoAHxAwQBBgwAA6gB6gP2Af8D6wH/A94B/wPWAf8D1QH/A9EB/wPDAf8DvAH/
+ A8AB/wPlAf8DmQHqLAADNgFIA5IB/gPUAf8DyAH/A7wB/wO6Af8DwgH/A8QB/wN6AdYQAAEyAUEBRQFT
+ ATgBvwHgAf8BigHWAesB/wG2AeoB+gH/AVUBzgHxAf8BQQHDAe4B/wEqAa8B6QH/ATYBsAHoAf8BQgG4
+ AegB/wExAXgBnQHYGAABCwE9AYcB/wF7AZcBuAH/AYoBtwHkAf8BcAGcAcgB/wEUAT8BbQH/ARgBQwFx
+ Af8BIQFEAWoB/wEUATwBZQH6AwUBBwwAA6oB6gP3Af8D5wH/A+8B/wP2Af8D+wH/A/oB/wPwAf8D3gH/
+ A8MB/wPmAf8DmgHqMAADlAH8A90B/wPFAf8DeAHBA3kBwwOsAf8D1wH/A4YB+xQAARsCHgElASoBsQHU
+ AfcBbAHPAekB/wG8AeoB+AH/AbkB6QH5Af8BcAHMAfAB/wFAAbMB6gH/AS8BmAHUAf8BPAFUAWEBfxgA
+ AQ4BSgGXAf8BEQFXAZ8B/wEOAUkBigH/AQ4BSgGHAf8BEAFKAYcB/wEUAUsBhQH/AREBQAF0Af8BGAE4
+ AV0B8RAAA6sB6gP4Af8D/hn/A/sB/wPqAf8DnAHqMAADlgH7A+QB/wPPAf8DegHCBAADjgH/A4wB/wOJ
+ AfscAAE/AXQBggGZATMBogHAAeIBLQGnAcsB8QEmAawB0wH9ATQBtgHiAf8BIQGOAcUB/gErAY0BxgH/
+ ATsBaQGBAbMUAAE4AUABSwF3AREBTgGVAf4BEQFXAZsB/wERAVcBmQH/ARABUgGTAf8BDgFJAYcB/wEO
+ AT4BcQH+ATkBPwFIAYEQAAOPAb8D4QH/A/4Z/wP7Af8DzwH/A3QBoTAAA4kB1gPiAf8D5wH/A7kB/wOT
+ Af8sAAEQAhEBFQEeASEBIgEqAQwCDQEQAT4BYwFuAYMBMQG0AeAB/wFDAbUB6wH/ASIBggG0AfsYAAE5
+ AUEBTgF9ARgBSgGKAfQBDwFKAZAB/wEOAUcBigH/ARYBQwF7AfUBOgFCAU0BhBQAAyIBKgOcAdED0AH/
+ A+gB/wPzAf8D/QH/A/wB/wPtAf8D4AH/A8IB/wOLAcMDFQEbMAADUgFwA5cB9QPkAf8D7gH/A5YB/zwA
+ AT4BhAGYAbQBJgGqAdEB+wE8AXMBiwGvSAADBQEHA0MBVQOEAa4DnQHWA7QB+wOyAfkDlQHNA34BqQM3
+ AUYDAgEDOAADUgFwA4oB1gOZAfsDlwH7KwABAQFJAUUBQgFkAZwBdgFXAfQBsgGAAVUB/wGvAX0BUQH/
+ AZwBcwFTAfYBUgFMAUcBc1gAAXkBYQFSAakBwwGOAWcB/wHAAYsBZQH/Ab4BiAFjAf8BuwGFAWAB/wG5
+ AYMBXgH/AbQBfQFbAf8BsQF6AVcB/wGuAXgBVgH/Aa0BdQFVAf8BqQFwAVAB/wF5AWEBUgGpWAABLwEt
+ ASwBPQGVAXcBXAHnAbIBgQFXAf8BywGrAYkB/wHRAbQBlQH/AbsBjgFiAf8BtQGHAVkB/wGrAXYBTAH/
+ AZMBcgFYAesBNQEyATEBRlAAAcgBkgFrKf8BqQFxAVAB/1AAAQ8BEAEPARQDBgEIAawBfwFWAf4B1QG7
+ AZ8B/wHWAbsBngH/AdMBuAGcAf8B0QGzAZQB/wG3AYkBXAH/AboBjgFhAf8BuAGNAWAB/wGyAYEBVQH/
+ AacBdQFNAf4BeQFhAVIBqQHDAY4BZwH/AcABiwFlAf8BvgGIAWMB/wG7AYUBYAH/AbkBgwFeAf8BtAF9
+ AVsB/wGyAXsBWQH/AbEBegFXAf8BrgF4AVYB/wGtAXUBVQH/AasBdAFTAf8BqQFyAVIB/wGpAXABUAH/
+ AXkBYQFSAakUAAHYAaIBeB//Af4C/wL+Bf8BtwGBAV0B/0cAAQEBQgFHAUIBYwFhAZEBZQH0AWIBogFp
+ Af8BYAGhAWgB/wGxAX0BUQH/AeEBzQG4Af8B2AHAAaUB/wHYAcABpwH/AdQBugGdAf8BuAGMAV8B/wG3
+ AYoBXwH/AbgBjQFgAf8BugGOAWEB/wGxAX0BUQH/AcgBkgFrNf8BqQFxAVAB/xQAAdkBowF4G/8B/gL/
+ Av4B/wP+Bf8BugGFAV8B/0AAASwBLgEtAT0BZgGNAWoB5wFlAaMBawH/AZMBwAGZAf8BngHHAaQB/wFw
+ AawBdwH/Aa8BfQFQAf8B4wHQAbwB/wHaAcMBqwH/AdMBuAGeAf8BxwGjAXwB/wHBAZgBbgH/AbYBiQFb
+ Af8BtwGKAV8B/wG6AY4BYQH/AbEBgAFTAf8BygGUAW0L/wH+A/8B/QH/Av4B/QH/Av4B/AH/Av4B/AH/
+ Av4B/AH/Av4B/AH/Av4B+gH/Av4B+gH/AvwB+QX/AaoBcgFSAf8MAAFsAVoBTwGUAa0BhQFmAeAB2wGk
+ AXkp/wG9AYcBYgH/QAABYwGeAWsB/gGpAc0BrwH/AaYBzAGsAf8BogHJAakB/wGZAcUBnwH/AWoBqQFz
+ Af8BrgF7AU4B/wHcAcgBsAH/Ab8BnwGBAf8BuAGNAWQB/wHRAbMBjwH/AdEBswGPAf8BuwGQAWUB/wG8
+ AZEBZwH/AbcBigFfAf8BsQF9AVEB/wHMAZcBbgf/AfwD/wH9Af8C/gH8Af8C/gH8Af8C/gH7Af8C/QH6
+ Af8C/QH6Af8C/QH6Af8C/QH6Af8C/AH3Af8C+wH2Bf8BrAF0AVMB/wwAAbEBhwFpAeAD2wHgAdwBpwF6
+ Af8B3AGnAXoB/wHcAacBegH/AdwBpwF6Af8B3AGnAXoB/wHcAacBegH/AdwBpwF6Af8B3AGnAXoB/wHc
+ AacBegH/AdwBpwF6Af8B3AGnAXoB/wHAAYsBZQH/BAADAgEDAyIBMANPAd8DZAH0A2sB9wNqAfkDPAFe
+ Ay8BQwNqAfIDawH3A2QB9ANPAd8DIgEwAwIBAwQAAWEBoQFoAf8BwAHaAcUB/wGtAdABswH/AasBzgGx
+ Af8BngHIAaYB/wFsAaoBdQH/AZUBegF9Af8BWQFgAcgB/wFPAVcB4wH/AU4BVQHgAf8BVwFeAcgB/wGQ
+ AXcBhAH/AbsBkAFlAf8B0QGzAY8B/wHGAaIBegH/AagBeAFPAf4B0QGcAXIF/wL+AfwB/wL+AfwB/wL+
+ AfwB/wL9AfsB/wL9AfsB/wL9AfoB/wL9AfgB/wL7AfkB/wH7AfoB9wH/AfsB+gH2Af8B+wH4AfQF/wGw
+ AXkBVwH/DAABvQGTAXQB4APbAeAB3QGtAYYB/wHoAbkBkgH/AegBuQGSAf8B6AG5AZIB/wHoAbkBkgH/
+ AegBuQGSAf8B6AG5AZIB/wHoAbkBkgH/AegBuQGSAf8B6AG5AZIB/wHoAbkBkgH/Ab8BkAFuAf0EAAMd
+ ASkDUgHXA4gB7wOhAe0DrgH/A5cB/wNwAf8DdwH/A5cB/wOuAf8DoQHtA4gB7wNSAdcDHQEpBAABXwGg
+ AWcB/wHFAd4ByQH/AbQB1AG5Af8BpAHJAaoB/wGBAasBmgH/AWABbAHDAf8BTwFXAeAB/wFlAWcB6wH/
+ ApMB9AH/AWABYgHqAf8BVwFaAeQB/wFIAVEB3AH/AV8BYgG+Af8BpgGJAX4B/wHBAZoBcAH/AYkBdQFm
+ AcgB1AGeAXQF/wL+AfwB/wL9AfsB/wL9AfwB/wL9AfsB/wL9AfkB/wL8AfgB/wH7AfkB9wH/AfsB+QH1
+ Af8B+wH4AfQB/wH7AfcB8gH/AfsB9QHyBf8BsgF7AVkB/wwAAb4BlAF0AeAD2wHgAcgBswGnAe0B3QGz
+ AZAB/gHcAacBegH/AdwBpgF5Af8B2gGkAXkB/wHYAaIBeAH/AdUBoAF1Af8B0gGdAXIB/wHPAZoBcQH/
+ Ac4BmQFvAf8BxAGaAXgB/wFRAUcBQQFrBAADOwFpA2YB7wNTAXQDVAG5A0IB+gNFAf8DUAH/A1AB/wNF
+ Af8DQgH6A1QBuQNTAXQDZgHvAzsBaQQAAVwBnwFkAf8BuQHWAb4B/wGHAboBjwH/AXABrAF3Af8BUgFY
+ AdwB/wFlAWkB6wH/AZgBlgH0Af8CkQHzAf8BiQGKAfAB/wFaAV4B5wH/AV4BYQHpAf8BXAFgAegB/wFQ
+ AVcB5AH/AUkBVAHXAf4BMwIxAUMDCAEKAdUBoAF1Bf8C/QH8Af8C/QH7Af8C/QH6Af8C/AH5Af8B/AH7
+ AfcB/wH7AfkB9QH/AfsB+AH0Af8B+wH3AfMB/wH7AfUB8gH/AfoB8wHvAf8B+AHyAewF/wG1AX0BWwH/
+ BAABYAFSAUkBggGZAXkBYwHEAdIBnwF3AfgB8QHrAegB+AHwAesB6AH4AfAB6gHnAfgB7wHqAecB+AHv
+ AeoB5wH4Ae8B6gHnAfgB7wHqAeYB+AHvAekB5gH4AecB5QHiAfAD2wHgAagBfwFiAeAMAANCAYoDgQH3
+ A0oBZwMIAQoDQgHzA70B/wPOAf8DwgH/A60B/wNCAfMDCAEKA0oBZwOBAfcDQgGKBAABZAGfAWsB/gGG
+ AboBjwH/AZkBxgGiAf8BcwGtAXsB/wFOAVYB4gH/AbQBsQH5Af8BlwGWAfQB/wKTAfQB/wGMAY0B8AH/
+ AVsBXwHoAf8BWwFgAecB/wFcAWAB6AH/AV4BYQHpAf8BTgFWAeIB/wIkASYBMAQAAdgBogF4Bf8C/QH6
+ Af8C/AH6Af8B/AH7AfkB/wH7AfoB9gH/AfsB+AH1Af8B+wH3AfQB/wH7AfYB8QH/AfgB9AHuAf8B9wHy
+ AesB/wH3AfAB6gH/AfYB7AHoBf8BtwGBAV0B/wQAAZwBfAFlAcQDvAHEAdkBrAGGAfgB2QGsAYYB+AHZ
+ AawBhgH4AdkBrAGGAfgB2QGsAYYB+AHZAawBhgH4AdkBrAGGAfgB2QGsAYYB+AHZAawBhgH4AdEBnwF2
+ AfgBwAGYAXYB4AGrAYIBZQHgDAADMQFOA3AB8APGAe4DVwG2A0oB/ANkAf8DkgH/A3gB/wNkAf8DSgH8
+ A1cBtgOYAeEDcAHwAzEBTgQAAWwBhQFvAcgBewG0AYUB/wF1Aa8BfQH/AW4BqwF3Af8BTQFTAeEB/wG0
+ AbEB+QH/AZUBlgH1Af8BZQFpAesB/wFuAXAB7AH/AW0BcQHsAf8BWQFbAeUB/wFbAWAB5wH/AV4BYQHp
+ Af8BUAFXAeIB/wIkASYBMAQAAdkBowF4Bf8B/AH7AfkB/wH8AfsB+AH/AfsB+QH3Af8B+wH3AfQB/wH6
+ AfcB8gH/AfkB9QHwAf8B9wHzAe0B/wH2Ae8B6gH/AfUB6wHnAf8B8wHqAeQB/wHyAecB3gX/AboBhQFf
+ Af8EAAGlAYUBbQHEA7wBxAHZAa8BjgH3AeMBuwGbAfgB4wG7AZsB+AHjAbsBmwH4AeMBuwGbAfgB4wG7
+ AZsB+AHjAbsBmwH4AeMBuwGbAfgB4wG7AZsB+AHcAbABiwH4AcoBpgGHAeABqQGEAWsB3gwAAwQBBgNK
+ AYoDkQH/A+gB/wPdAf8DwQH/A3kB3gNtAcID0wH4A90B/wPEAf8DkQH/A0oBigMEAQYEAAMIAQoBKwEt
+ ASsBOwE+AUIBPgFbAW0BqAF2Af8BSwFRAeAB/wKiAfQB/wFpAWsB7AH/AWABYgHqAf8BlwGTAfcB/wGX
+ AZMB9wH/AWMBZwHpAf8BZAFlAeoB/wFbAWAB5wH/AU4BVgHiAf8CJAEmATAEAAHbAaQBeTX/Ab0BhwFi
+ Af8EAAGmAYUBbQHEA7wBxAG5AagBngHaAdgBtAGXAfYB2QGsAYYB+AHZAasBhQH4AdgBqQGFAfgB1gGn
+ AYQB+AHTAaUBgQH4AdABowF/AfgBzgGgAX4B+AHGAZUBbQH4Aa4BjQF0AeABRwFAATwBXhAAAwgBCwNK
+ AXUDgQHuA54B/wN2AeEDKAE3AxIBGAN1Ad0DngH/A4EB7gNKAXUDCAELGAABVAFaAdoB/gJ7AfIB/wGX
+ AZMB9wH/AWMBZwHpAf8BUQFXAeMB/wFRAVcB4wH/AWMBZwHpAf8BlwGTAfcB/wJ7AfIB/wFNAVYB2AH+
+ Ah0BHwEmBAAB3AGnAXoB/wHcAacBegH/AdwBpwF6Af8B3AGnAXoB/wHcAacBegH/AdwBpwF6Af8B3AGn
+ AXoB/wHcAacBegH/AdwBpwF6Af8B3AGnAXoB/wHcAacBegH/AdwBpwF6Af8B3AGnAXoB/wHcAacBegH/
+ AcABiwFlAf8EAAGmAYYBbgHEA7wBxAO8AcQDvAHEA7wBxAO8AcQDvAHEA7wBxAO8AcQDvAHEA7wBxAGV
+ AXUBXwHEYAABawFuAaMByAFtAW4B7AH/AWUBZwHrAf8BXgFhAekB/wJ3AfAB/wJzAfAB/wFeAWEB6QH/
+ AWgBagHrAf8BbgFwAewB/wFqAW0BoQHICAAB2wGrAYUB/QHoAbkBkgH/AegBuQGSAf8B6AG5AZIB/wHo
+ AbkBkgH/AegBuQGSAf8B6AG5AZIB/wHoAbkBkgH/AegBuQGSAf8B6AG5AZIB/wHoAbkBkgH/AegBuQGS
+ Af8B6AG5AZIB/wHoAbkBkgH/Ab8BkAFuAf0EAAGnAYgBbwHEAacBiAFvAcQBpwGIAW8BxAGnAYgBbwHE
+ AacBiAFvAcQBpwGIAW8BxAGnAYgBbwHEAacBiAFvAcQBpwGIAW8BxAGnAYgBbwHEAacBiAFvAcQBlwF4
+ AWEBxGAAAwgBCgErASwBLwE7AT4BPwFIAVsBYAFiAeMB/wFQAVYB4gH/AVABVgHiAf8BXgFhAeMB/wE/
+ AUABSgFeASsBLAEvATsDCAEKCAABUQFHAUEBawHSAaoBigH0AdwBpwF6Af8B3AGmAXkB/wHaAaQBeQH/
+ AdgBogF4Af8B1QGgAXUB/wHUAZ4BdAH/AdIBnQFyAf8BzwGaAXEB/wHOAZkBbwH/AcsBlgFuAf8ByQGU
+ AWsB/wG8AZUBeAH0AVEBRwFBAWsEAAGlAYkBcwHCAa4BkwF8AcQBrgGTAXwBxAGuAZMBfAHEAa4BkwF8
+ AcQBrgGTAXwBxAGuAZMBfAHEAa4BkwF8AcQBrgGTAXwBxAGuAZMBfAHEAa4BkwF8AcQBlgF6AWYBwtAA
+ AT8BOQE2AVIBoAGIAXUBvAGnAYgBbwHEAacBiAFuAcQBpgGGAW4BxAGjAYQBawHEAaMBggFrAcQBoQGC
+ AWkBxAGgAYABaQHEAZ8BgAFoAcQBmQGAAW0BxAE/ATkBNgFSUAACEgERARcBqwFoATwB/wGiAV8BOwH2
+ ATABLAEqAT90AAMGAQgBSQFRAVsBbgFjAXsBlgGzAWYBiwG1Ad0BXAGNAb8B9AFOAYYBvQH9AT4BegGz
+ Af8BLgFvAagB/wEiAWUBngH9ARoBXQGRAfQBIAFYAYQB3QExAVYBcgGzATUBRAFOAW4DBgEICAABsgF7
+ AVkC1QGDAVAB/wHRAXwBSgH/AY8BZgFNAa8sAAG6AXoBSQH/AcABiQFeAf8BvwGJAWEB/wGvAWwBRgH/
+ ATIBLQErAUFwAAFoAYMBpAHEAYcBtwHgAf8BlQHGAeYB/wGbAc0B6QH/AZwBzQHpAf8BmQHHAecB/wGV
+ AcEB5AH/AY4BuAHgAf8BiQGwAd0B/wGBAacB1wH/AXIBmwHPAf8BXAGMAcIB/wE9AXcBrgH/ASwBWAF6
+ AcQEAAGsAX4BYgHMAd8BpAGBAf8B6gHCAasB/wHqAcABqAH/AdwBmQFwAf8BjwFnAU4BrygAAcMBiAFY
+ Af8BzwGiAXwB/wHNAaIBgAH/AcABjAFlAf8BsAFxAUgB/wFrAVMBRgGVATsBNQExAU4BLAEoAScBOQEX
+ AhYBHgMDAQRcAAFoAYMBpAHEAZgBuQHUAf8BxQHHAcQB/wHsAdgBxwH/AfYB6wHjAf8B+gH1AfEB/wHz
+ AecB3gH/Ad8BvwGmAf8B0AGkAYAB/wHLAZoBcwH/AcgBlwFwAf8BowGTAYwB/wFhAX4BnAH/ASwBWAF6
+ AcQEAAHbAZcBbgH/Ae0ByAGzAf8B5wG4AZsB/wHmAbQBmAH/AeoBwwGrAf8B3gGcAXIB/wGPAWgBTwGv
+ JAABWwFQAUcBcQHMAZkBcAH/AdABowGBAf8BzwGkAYMB/wHKAZ4BegH/AbwBhQFcAf8BrwFwAUgB/wGn
+ AWMBPwH/AZ4BVgE3Af8BfwFQAUABxgMHAQlYAAIiASEBKgHmAcMBpQH/Ae8B1gHAAf8B+wHyAeoB/wH+
+ AfwB+gL/Af4B/QH/Af4B/QH8Af8B/AH2AfAB/wH4AegB2QH/AfYB4wHRAf8B9gHiAc4B/wHmAcgBrQH/
+ Ac4BoAF6Af8BIQIgASoEAAHdAZ8BeAH/Ae0BzAG3Af8B6AG9AaMB/wHkAbEBkgH/AeYBtgGaAf8B6gHD
+ AawB/wHeAZwBcwH/AY4BaAFRAawkAAFdAVEBSQFyAc4BnQF1Af8B1QGsAYwB/wHLAZsBdQH/AcwBoAF7
+ Af8ByAGbAXUB/wHFAZUBbgH/AcABjwFoAf8BrAFsAUcB/wFUAUUBPwF2FAADfwG3A6AB/wObAf8DlwH/
+ A5IB/wOOAf8DigH/A4UB/wOBAf8DfAH/A3gB/wN0Af8DcQH/A20B/wNqAf8DYQG3CAABpAGQAX4BuQHt
+ AdMBuwH/AfoB8AHlAf8B/QH5AfUB/wH+AfwB+gH/Af4B/QH8Af8B/QH5AfQB/wH2AeMBzwH/AfcB5QHT
+ Af8B9gHjAdEB/wHdAbkBmgH/AZQBegFmAbgIAAF8AWkBXQGSAeYBswGUAf8B7gHMAbgB/wHpAb4BpQH/
+ AeUBswGUAf8B5gG3AZsB/wHqAcQBrQH/Ad4BngF3Af8BjgFpAVEBrCQAAZABdgFiAbEB0wGoAYYB/wHW
+ AawBjgH/AckBmAFwAf8BxAGQAWcB/wG/AYoBXgH/AcIBjwFmAf8BvwGLAWMB/wF+AWYBRwH7ASABHgEd
+ ASkQAAOpAf8D6QH/A9MB/wPSAf8D0QH/A9AB/wPOAf8DzQH/A80B/wPLAf8DywH/A8oB/wPJAf8DyAH/
+ A+IB/wNqAf8MAAGbAYoBewGwAegBxwGrAf4B9wHqAd4B/wH+AfoB9wH/Af4B/AH5Af8B/QH3AfIB/wH6
+ Ae0B4gH/AesB0AG3Af8B0AGhAX4B/gGOAXcBZgGuEAABfAFpAV4BkgHmAbQBmAH/Ae4BzQG6Af8B6QG/
+ AaUB/wHlAbQBlgH/AecBuQGdAf8B6wHGAa4B/wHeAZ8BeAH/AY0BaAFSAaogAAFAATwBOAFOAdQBowF9
+ Af8B3AG1AZgB/wHQAaEBfAH/AcwBmgFzAf8BzwGkAYMB/wHIAZoBdQH/AXwBjAFjAf8BaQG4AXkB/wGL
+ AVABNQH7AXABSwFAAa8DBwEJCAADrgH/A9YB/wOxAf8DqAH/A6YB/wOkAf8DogH/A6EB/wOeAf8DnAH/
+ A5wB/wOaAf8DmQH/A6IB/wPIAf8DbgH/EAABMQEvAS0BOgHLAbQBnwHhAfMB4gHSAf8B/AH0Ae0B/wH7
+ AfEB5wH/AekBzAG1Af8BuQGYAX4B3AEuASwBKgE4GAABfQFqAV8BkgHmAbcBmQH/Ae4BzgG7Af8B6QHA
+ AacB/wHoAb0BowH/AewByAGzAf8B3wGkAYEB/wGmAXgBXAHJIAABLwEtASsBOQHbAasBiQH/AeEBvQGi
+ Af8B1gGqAYcB/wHZAbMBlAH/Ac4BnwF5Af8BlAGIAV8B+wFsAbwBewH/AZMBbgFFAf8BsAF0AU4B/wGk
+ AWEBQAH/AYMBSgE5AdoDAwEEBAADsgH/A9kB/wNwAf8DZgH/A2UB/wNjAf8DYwH/A2IB/wNgAf8DYAH/
+ A18B/wNdAf8DXAH/A3cB/wPKAf8DcgH/GAABsgGdAYoBxwH0AeMB0AH/AfMB3wHNAf8BqAGPAXwBwyQA
+ AX0BawFfAZIB5gG3AZwB/wHvAc8BvAH/Ae4BzgG6Af8B4gGuAYwB/wHSAZsBegH/A4cB9wMbASMcAAEZ
+ ARgBFwEeAeABsQGPAf8B5gHEAasB/wHiAb8BpAH/AdgBrQGOAf8BpgGSAXAB+gFaAVIBSAF3AaIBdwFH
+ AfwBuwGEAVwB/wHAAY8BaAH/AbwBigFgAf8BnAFRATQB/wEXAhYBHgQAA7cB/wPdAf8DtwH/A60B/wOs
+ Af8DqgH/A6gB/wOmAf8DpQH/A6MB/wOhAf8DngH/A50B/wOoAf8DzwH/A3YB/xgAAa8BmQGHAcMB9AHh
+ Ac4B/wHzAd8BzAH/AagBkAF8AcIoAAF9AWsBYAGSAecBuQGcAf8B5gG2AZgB/wHYAakBjAH/A9IB/wO1
+ Af8DiAH+A4YB+wOEAfsDeQHWA00BcAwAAwMBBAHCAaABiQHaAeQBuwGfAf8B5AG7AZ8B/wGeAaQBgwH8
+ AXUBwQF+Af8BrgGNAVkB/AHHAZYBbgH/AcsBngF6Af8BvAGFAVgB/wHDAZIBawH/AaYBYgE+Af8BLAEo
+ AScBOQQAA48BvQPaAf8DyAH/A8EB/wPAAf8DvgH/A7sB/wO5Af8DuAH/A7YB/wO0Af8DsQH/A7AB/wO0
+ Af8DwwH/A20BvRAAAS8BLgEsATgBzAGzAZ0B3QHzAd0ByQH/AfoB7gHiAf8B+QHrAd4B/wHtAdABtgH/
+ Ab8BogGIAdsBLwEtASsBOCQAAX4BbAFhAZIBsAGPAXoBzwORAfsDxwH/A8wB/wPHAf8DxgH/A8MB/wPA
+ Af8DggH1A00BcAwAAwcBCQGZAYMBdgGvAbYBrwGQAfkBdgHFAYIB/wG1AbABgQH/AdgBsAGSAf8B1wGu
+ AY8B/wHJAZcBbgH/AcMBjwFlAf8ByAGbAXUB/wGxAXABSQH/ATwBNQExAU4EAAOSAb0D4wH/A+cB/wPh
+ Af8D4AH/A+AB/wPfAf8D3wH/A94B/wPdAf8D3QH/A9wB/wPbAf8D4AH/A9AB/wNvAb0MAAGhAZIBhAGv
+ AfQB1QG4Af4B+QHsAeAB/wH9AfgB9AH/AfsB8AHmAf8B+AHnAdcB/wH5AesB3gH/AfIB2gHFAf8B4QG4
+ AZYB/gGVAYIBcgGuKAADNgFIA5IB/gPUAf8DyAH/A7wB/wO6Af8DwgH/A8QB/wN6AdYUAAIfAR4BJgHD
+ AbIBhgH9AeIBvgGjAf8B3wG3AZoB/wHVAagBhgH/AdABoQF8Af8BywGaAXIB/wHOAaIBgAH/Ab8BiwFh
+ Af8BbgFXAUcBlQQAAzMBPwPOAf8D7AH/A94B/wPYAf8D0gH/A8wB/wPIAf8DxwH/A8gB/wPLAf8D0AH/
+ A90B/wPoAf8DpQH/AzEBQggAAawBnAGNAbgB+QHjAc0B/wH7AfMB7AH/Af4B+gH3Af8B/gH7AfgB/wH8
+ AfUB7QH/AfcB5gHVAf8B9gHhAcwB/wH5AewB3wH/AfkB6wHdAf8B6wHOAbQB/wGeAYkBeAG4KAADlAH8
+ A90B/wPFAf8DeAHBA3kBwwOsAf8D1wH/A4YB+xgAAWUBWwFUAXYB5AG6AZ0B/wHmAcQBqwH/AeIBvgGk
+ Af8B3gG5AZwB/wHZAbIBkwH/AdEBowF+Af8B0QGmAYUB/wG7AX4BUAH/ATEBLQErAT8EAAOUAb0D4wH/
+ A9sB/wPiAf8D4gH/A+EB/wPgAf8D4AH/A+AB/wPfAf8DwwH/A9EB/wPTAf8DdgG9CAABIwIiASoBwAHF
+ Ac0B/wGaAbcB3AH/AXwBpgHVAf8BYgGVAcoB/wFOAYcBvgH/AT4BegGzAf8BLgFvAagB/wEiAWYBnwH/
+ AR0BYgGYAf8BKwFqAZoB/wFSAYMBpwH/AYgBlAGaAf8CIQEgASokAAOWAfsD5AH/A88B/wN6AcIEAAOO
+ Af8DjAH/A4kB+xgAAwcBCQGvAZMBgAHGAeMBtAGTAf8B3wGxAY4B/wHaAasBiQH/AdoBrQGMAf8B3AG1
+ AZgB/wHXAa8BkAH/AcwBmwFzAf8BswF3AUUB+gQAAzMBPwPRAf8D8wH/A+0B/wPtAf8D7QH/A+0B/wPt
+ Af8D7AH/A+wB/wPsAf8D8QH/A7AB/wMyAUIIAAFxAY0BrgHRAYcBtwHgAf8BlQHGAeYB/wGbAc0B6QH/
+ AZwBzQHpAf8BmQHHAecB/wGVAcEB5AH/AY4BuAHgAf8BiQGwAd0B/wGBAacB1wH/AXIBmwHPAf8BXAGM
+ AcIB/wE9AXcBrgH/AS8BXQGAAdEkAAOJAdYD4gH/A+cB/wO5Af8DkwH/KAADAwEEARkCGAEeATABLQEs
+ ATkBQQE8ATkBTgF8AWwBXwGUAdgBpwGCAf8B1wGsAYsB/wHTAacBhAH/AboBggFWAfYIAAOWAb0DxQH/
+ A8EB/wO+Af8DugH/A7YB/wOyAf8DrgH/A6kB/wOlAf8DoQH/A34BvQwAAWgBgwGkAcQBhwG3AeAB/wGV
+ AcYB5gH/AZ0BzwHqAf8BoQHTAewB/wGgAdEB6wH/AZsBygHoAf8BlgHCAeUB/wGPAboB4gH/AYcBsQHd
+ Af8BeAGjAdQB/wFgAZABxAH/AT0BdwGuAf8BLAFYAXoBxCQAA1IBcAOXAfUD5AH/A+4B/wOWAf88AAEz
+ ATABLgE+AdABngF6AfsByAGWAW4B9gE6ATYBMgFIRAADBgEIAUkBUQFbAW4BYwF7AZYBswFsAZABuQHd
+ AW8BmgHNAfQBcAGfAdUB/QFoAZkB0QH/AVsBkAHIAf8BTgGGAb0B/QFBAXkBrAH0ATsBawGXAd0BPAFf
+ AXoBswE3AUUBTwFuAwYBCCgAA1IBcAOKAdYDmQH7A5cB+xAAATUBnQHZAf8BMAGZAdgB/wErAZQB1wH/
+ AScBkAHWAf8BIgGMAdUB/wEdAYgB1AH/ARkBhAHTAf8BFAGAAdIB/wEQAXsB0QH/AQ0BeAHRAf8BCQF1
+ AdAB/wEGAXIBzwH/AQMBbwHPAf8BAAFtAc4B/xwAA0IBYwNYAb8DVQG/Az4BYzAAAwoBDQMmATEDJAEv
+ AwUBBxgAAhIBEQEXAasBaAE8Af8BogFfATsB9gEwASwBKgE/NAABPAGjAdoB/wG8AesB+gH/AbwB6wH8
+ Af8BvwHuAf4B/wHGAfQC/wHOAfgC/wHTAfoC/wHQAfgC/wHHAfIC/wG6AekB/AH/AbMB5AH5Af8BsAHi
+ AfgB/wGwAeIB+AH/AQQBcAHPAf8QAAMfASkDCwEOAwEBAgN8AeoDvQH/A7IB/wNcAeoDAQECAwsBDgMe
+ ASkYAAMaASEDbwGdA5gB8AOhAf8DqwH/A6cB/wOVAf8DhgHnA1wBigMRARYMAAG6AXoBSQH/AcABiQFe
+ Af8BvwGJAWEB/wGvAWwBRgH/ATIBLQErAUEwAAFCAagB2wH/Ab8B7AH7Af8BWAHPAfUB/wFAAbAB7AH/
+ AU0BugHvAf8BWQHCAe8B/wFfAcYB7wH/AVsBxAHvAf8BSwG2Ae8B/wE2AaUB5gH/ASkBmgHhAf8BNwG4
+ Ae4B/wGxAeMB+AH/AQgBdAHQAf8MAANiAZsDbgH9A2QB5wMTARkDfgHnA8sB/wPHAf8DYgHnAxMBGQNa
+ AecDTgH9A0wBmxAAAzkBSgOgAfQDzgH/A+0B/wP0Af8D9QH/A/QB/wPvAf8D4gH/A7oB/wODAecDKAE1
+ CAABwwGIAVgB/wHPAaIBfAH/Ac0BogGAAf8BwAGMAWUB/wGwAXEBSAH/AWsBUwFGAZUBOwE1ATEBTgEs
+ ASgBJwE5ARcCFgEeAwMBBBwAAUgBrQHcAf8BwQHuAfsB/wFeAdMB9wH/AWsB2wH8Af8BfgHlAv8BjwHt
+ Av8BlwHyAv8BkwHtAv8BewHfAv8BWgHMAfgB/wFFAb4B7wH/ATsBugHuAf8BswHjAfkB/wENAXgB0QH/
+ CAADWgF7A7wB/wPeAf8DpgH/A4AB9AOEAf4DxAH/A8IB/wNtAf4DbAH0A6YB/wPSAf8DgAH/A0gBewwA
+ A54B6gPeAf8D8wH/A9sB/wPSAf8D2wH/A9YB/wPAAf8DyQH/A+YB/wPEAf8DhQHqCAABWwFQAUcBcQHM
+ AZkBcAH/AdABowGBAf8BzwGkAYMB/wHKAZ4BegH/AbwBhQFcAf8BrwFwAUgB/wGnAWMBPwH/AZ4BVgE3
+ Af8BfwFQAUABxgMHAQkYAAFNAbIB3QH/AcMB7wH7Af8BZAHWAfgB/wFLAbYB7AH/AVkBvQHvAf8BlQHr
+ Av8BLwGXAd0B/wFMAYIBqwH/AYQB4QL/AUABqQHpAf8BMQGfAeEB/wFBAb4B7wH/AbQB5QH5Af8BEgF9
+ AdIB/wgAA14BfQOlAf4D1QH/A8UB/wPLAf8D0QH/A8kB/wPHAf8DzAH/A8UB/wO9Af8DywH/A24B/gNN
+ AX0MAAOgAeoD8AH/A94B/wPUAf8D0gH/A9sB/wPWAf8DvwH/A7AB/wOzAf8D3gH/A4kB6gwAAV0BUQFJ
+ AXIBzgGdAXUB/wHVAawBjAH/AcsBmwF1Af8BzAGgAXsB/wHIAZsBdQH/AcUBlQFuAf8BwAGPAWgB/wGs
+ AWwBRwH/AVQBRQE/AXYYAAFSAbcB3gH/AcYB8AH8Af8BaQHZAfgB/wF7AeIB/QH/AZAB6AL/AZkB6QL/
+ ATEBnwHfAf8BUwGLAbIB/wGKAeIC/wFpAdAB+QH/AU8BxQHxAf8BRQHBAfAB/wG2AecB+QH/ARcBgwHT
+ Af8MAANjAYUDxQH/A8EB/wPFAf8DxwH/A6oB/wOnAf8DwQH/A74B/wO1Af8DqgH/A1EBhRAAA6IB6gPy
+ Af8D4gH/A9gB/wPVAf8D3AH/A9gB/wPAAf8DswH/A7cB/wPgAf8DjgHqEAABkAF2AWIBsQHTAagBhgH/
+ AdYBrAGOAf8ByQGYAXAB/wHEAZABZwH/Ab8BigFeAf8BwgGPAWYB/wG/AYsBYwH/AX4BZgFHAfsBIAEe
+ AR0BKRQAAVcBuwHfAf8BxwHxAfwB/wFuAdwB+QH/AVUBuwHtAf8BYAG9Ae8B/wGbAecC/wE0AaYB4gH/
+ AUoBpAHhAf8BkAHiAv8BSAGtAekB/wE3AaQB4wH/AUgBxAHwAf8BuAHoAfkB/wEdAYgB1AH/BAADigHN
+ A4UB4wOYAe4DzwH/A8YB/wPMAf8DhAHGAzQBRAMzAUQDegHGA8EB/wO8Af8DuQH/A2MB7gNZAeMDVgHN
+ CAADpAHqA/MB/wPnAf8D3QH/A9kB/wPgAf8D2wH/A8QB/wO4Af8DuwH/A+EB/wOSAeoQAAFAATwBOAFO
+ AdQBowF9Af8B3AG1AZgB/wHQAaEBfAH/AcwBmgFzAf8BzwGkAYMB/wHIAZoBdQH/AXwBjAFjAf8BaQG4
+ AXkB/wGLAVABNQH7AXABSwFAAa8DBwEJDAABWwG/AeAB/wHIAfMB/AH/AXQB3wH5Af8BiQHmAf0B/wGV
+ AecC/wGaAeUC/wGqAe4C/wGoAe0C/wGZAeMC/wFzAdUB+QH/AVgBzAHzAf8BTgHIAfEB/wG7AekB+gH/
+ ASMBjQHVAf8EAAO+Af0D4gH/A9IB/wPGAf8DzQH/A7EB/wMzAUQIAAM0AUQDqAH/A8IB/wO3Af8DwAH/
+ A9IB/wNgAf0IAAOlAeoD9AH/A+oB/wPhAf8D3QH/A+MB/wPeAf8DyQH/A70B/wO/Af8D4gH/A5QB6hAA
+ AS8BLQErATkB2wGrAYkB/wHhAb0BogH/AdYBqgGHAf8B2QGzAZQB/wHOAZ8BeQH/AZQBiAFfAfsBbAG8
+ AXsB/wGTAW4BRQH/AbABdAFOAf8BpAFhAUAB/wGDAUoBOQHaAwMBBAgAAV8BwgHhAf8ByQHzAfwB/wHL
+ AfMB/QH/AdQB9gH+Af8B1wH2Av8B2AH0Av8B4AH4Av8B3wH4Av8B2gH1Av8BzQHxAfwB/wHCAe0B+gH/
+ Ab0B6wH6Af8BvQHrAfoB/wEqAZMB1gH/BAADwgH9A+kB/wPWAf8DyQH/A84B/wOlAf8DMgFECAADNAFE
+ A6wB/wPEAf8DugH/A8YB/wPdAf8DagH9CAADpwHqA/UB/wPuAf8D5gH/A+IB/wPmAf8D4QH/A80B/wPC
+ Af8DwgH/A+MB/wOWAeoQAAEZARgBFwEeAeABsQGPAf8B5gHEAasB/wHiAb8BpAH/AdgBrQGOAf8BpgGS
+ AXAB+gFaAVIBSAF3AaIBdwFHAfwBuwGEAVwB/wHAAY8BaAH/AbwBigFgAf8BnAFRATQB/wEXAhYBHggA
+ AWABwwHhAf8BiAGgAagB/wORAf8DjgH/AVkBuQHcAf8BVAG4Ad8B/wFQAbUB3gH/AUwBsQHdAf8BSAGt
+ AdwB/wFFAagB1wH/A3cB/wN1Af8BZAF9AY0B/wEwAZkB2AH/BAADogHNA68B4wO0Ae4D2AH/A80B/wO8
+ Af8DcwHGAzEBRAMyAUQDegHGA8MB/wPCAf8DzQH/A4cB7gN/AeMDdQHNCAADqAHqA/YB/wPrAf8D3gH/
+ A9YB/wPVAf8D0QH/A8MB/wO8Af8DwAH/A+UB/wOZAeoQAAMDAQQBwgGgAYkB2gHkAbsBnwH/AeQBuwGf
+ Af8BngGkAYMB/AF1AcEBfgH/Aa4BjQFZAfwBxwGWAW4B/wHLAZ4BegH/AbwBhQFYAf8BwwGSAWsB/wGm
+ AWIBPgH/ASwBKAEnATkMAAN/AcMDxgH/A5QB/wMGAQgQAAMGAQgDfAH/A6sB/wNrAcMQAANqAYUD1AH/
+ A8wB/wPJAf8DugH/A5wB/wOhAf8DwgH/A8YB/wPBAf8DtwH/A1kBhRAAA6oB6gP3Af8D5wH/A+8B/wP2
+ Af8D+wH/A/oB/wPwAf8D3gH/A8MB/wPmAf8DmgHqFAADBwEJAZkBgwF2Aa8BtgGvAZAB+QF2AcUBggH/
+ AbUBsAGBAf8B2AGwAZIB/wHXAa4BjwH/AckBlwFuAf8BwwGPAWUB/wHIAZsBdQH/AbEBcAFJAf8BPAE1
+ ATEBTgwAA3YBrgPEAf8DoQH/AyABKRAAAx8BKQOJAf8DqQH/A2YBrgwAA2UBfQPDAf4D3AH/A9QB/wPZ
+ Af8D2wH/A9YB/wPUAf8D2QH/A9IB/wPLAf8DyAH/A3kB/gNQAX0MAAOrAeoD+AH/A/4Z/wP7Af8D6gH/
+ A5wB6hwAAh8BHgEmAcMBsgGGAf0B4gG+AaMB/wHfAbcBmgH/AdUBqAGGAf8B0AGhAXwB/wHLAZoBcgH/
+ Ac4BogGAAf8BvwGLAWEB/wFuAVcBRwGVDAADVgF1A7oB/wO/Af8DigHdAxgBHwMEAQUDBAEFAxgBHwOA
+ AeUDqAH/A54B/wNOAXUMAANkAXsD3AH/A+0B/wPbAf8DugH0A70B/gPWAf8D1AH/A68B/gOmAfQDywH/
+ A+cB/wO3Af8DVQF7DAADjwG/A+EB/wP+Gf8D+wH/A88B/wN0AaEgAAFlAVsBVAF2AeQBugGdAf8B5gHE
+ AasB/wHiAb4BpAH/Ad4BuQGcAf8B2QGyAZMB/wHRAaMBfgH/AdEBpgGFAf8BuwF+AVAB/wExAS0BKwE/
+ CAADCQEMA5AB2wPEAf8DvgH/A6EB/wOWAf8DkwH/A5cB/wOuAf8DrgH/A3sB2wMHAQkQAAN/AZsDzAH9
+ A7gB5wMUARkDsQHnA94B/wPdAf8DpAHnAxQBGQOfAecDpwH9A24BmxAAAyIBKgOcAdED0AH/A+gB/wPz
+ Af8D/QH/A/wB/wPtAf8D4AH/A8IB/wOLAcMDFQEbIAADBwEJAa8BkwGAAcYB4wG0AZMB/wHfAbEBjgH/
+ AdoBqwGJAf8B2gGtAYwB/wHcAbUBmAH/AdcBrwGQAf8BzAGbAXMB/wGzAXcBRQH6DAADJgEwA5IB3gO8
+ Af8DygH/A8wB/wPKAf8DwgH/A60B/wOCAd4DJQEwGAADIQEpAwsBDgMBAQIDtwHqA+UB/wPkAf8DoAHq
+ AwEBAgMLAQ4DIAEpGAADBQEHA0MBVQOEAa4DnQHWA7QB+wOyAfkDlQHNA34BqQM3AUYDAgEDKAADAwEE
+ ARkCGAEeATABLQEsATkBQQE8ATkBTgF8AWwBXwGUAdgBpwGCAf8B1wGsAYsB/wHTAacBhAH/AboBggFW
+ AfYQAAMJAQwDXgGBA38BugOGAcwDhAHMA3oBugNaAYEDCQEMKAADUAFjA5cBvwOVAb8DTgFjiAABMwEw
+ AS4BPgHQAZ4BegH7AcgBlgFuAfYBOgE2ATIBSIgAAT0BjAFEAf8BOQGHAUAB/wE2AYIBPAH/ATIBfQE4
+ Af80AANxAZoDPAFOGAADNgFNAzQBSgMCAQMIAAE4AVgBcQG3ARoBVwGIAf8BGQFVAYYB/wEZAVQBhQH/
+ ARgBUwGDAf8BGAFTAYIB/wEXAVIBgAH/ARcBUQF+Af8BFgFQAX0B/wEWAU8BfAH/ARUBTgF6Af8BFQFN
+ AXkB/wEVAU0BeAH/ARUBTAF4Af8BFAFMAXcB/wEzAVEBawHASAABQQGRAUgB/wGBAcUBhwH/AX0BwwGF
+ Af8BNgGDAT0B/zQAAzsBSwObAeQDWwF8EAADUwGAA2cBwAMxAUUMAAEjAVoBhwHwAX0BrQHgAf8BgQGv
+ AeQB/wF+AasB4gH/AX0BqAHgAf8BewGmAd8B/wF5AaIB3gH/AXgBoAHdAf8BdgGeAdwB/wF1AZsB2wH/
+ AXQBmQHaAf8BcwGYAdoB/wFzAZgB2gH/AXMBmAHaAf8BcwGYAdoB/wEUAUwBdwH/BAABPAFNAV0BcAFC
+ AWoBjAGmATUBgQHFAeoBMQGHAdEB9wExAYcB0QH3ATEBhwHRAfcBMQGHAdEB9wExAYcB0QH3ATEBhwHR
+ AfcBMQGHAdEB9wExAYcB0QH3ATEBhgHRAfcBMwGFAcwB8QFBAV8BewGTDAABRQGXAU0B/wGFAccBiwH/
+ AYIBxgGJAf8BOgGJAUEB/zgAA4oBvAOmAfcDbgGcCAADZwGjA30B8wNlAa4QAAEjAVsBhwHwAYABsgHj
+ Af8BUAGUAdwB/wFDAYoB2AH/AUEBhQHVAf8BPwGAAdQB/wE8AXsB0QH/AToBdgHPAf8BNwFyAc0B/wE1
+ AW8BzAH/ATMBbAHKAf8BMQFoAckB/wEwAWYByAH/AS8BZAHIAf8BcwGYAdoB/wEVAUwBeAH/BAABPAGG
+ AcMB5gHOAeAB6AHwAacB2wHyAf0BngHbAfQB/wGWAdoB8wH/AY4B2AHzAf8BhgHXAfMB/wF+AdQB8gH/
+ AXgB0wHyAf8BcQHSAfEB/wFrAdAB8QH/AWgBzwHxAf8BwQHpAfcB/gE4AYkBywHwDAABSQGcAVEB/wGJ
+ AcoBkAH/AYYByAGNAf8BPgGOAUYB/zgAAyYBMAOyAf0DqQH+A4UBxAOAAcQDjgH+A4QB9AMcASQQAAEk
+ AVwBiQHwAYMBtwHlAf8BVAGdAd8B/wFJAZIB2wH/AUYBjQHZAf8BQwGJAdcB/wFAAYMB1QH/AT4BfgHT
+ Af8BOwF6AdAB/wE5AXUBzgH/ATYBcQHNAf8BNAFtAcsB/wEyAWoBygH/ATEBZwHJAf8BdAGYAdoB/wEV
+ AU0BeQH/BAABNwGRAdMB9wHvAfoB/gH/AaEB6QH5Af8BkQHlAfgB/wGBAeEB9wH/AXEB3gH2Af8BYgHa
+ AfUB/wFTAdcB9AH/AUYB0wHzAf8BOAHQAfIB/wEtAc0B8QH/ASUBywHwAf8BygHyAfsB/wE3AZEB0wH3
+ DAABTAGiAVUB/wGNAcsBlAH/AYoBywGRAf8BQgGUAUoB/zwAA3QBlgOxAfwBzwHrAv8BywHpAv8DlwH8
+ A1wBhxQAASQBXQGKAfABhQG9AeYB/wFYAaQB4wH/AU4BmwHeAf8BSwGWAdwB/wFIAZEB2gH/AUYBjAHY
+ Af8BQgGHAdYB/wE/AYIB1AH/AT0BfAHRAf8BOwF5AdAB/wE3AXMBzgH/ATYBcAHMAf8BMwFtAcsB/wF1
+ AZsB2wH/ARUBTgF6Af8EAAE5AZgB1AH4AfIB+gH9Af8BswHtAfoB/wGkAekB+QH/AZUB5gH4Af8BhQHi
+ AfcB/wF1Ad4B9gH/AWQB2wH1Af8BVgHXAfQB/wFIAdQB8wH/AToB0QHyAf8BLwHOAfEB/wHMAfIB+wH/
+ ATcBlQHTAfcMAAFQAacBWgH/AZABzwGZAf8BjgHMAZYB/wFGAZkBTwH/KAABRAFQAWQBbwFAAUsBWwFm
+ AwwBDwgAAw4BEgO0AfMDsgH+A6kB/gOWAeUDCQEMDAABKQEvATYBPwEyATwBSwFVASUBXQGLAfABiAHB
+ AegB/wFdAawB5gH/AVMBowHjAf8BTwGeAeEB/wFMAZkB3gH/AUoBlAHcAf8BRwGPAdoB/wFDAYoB2AH/
+ AUEBhQHVAf8BPwGAAdQB/wE8AXsB0QH/AToBdgHPAf8BNwFyAc0B/wF3AZ8B3QH/ARYBTwF7Af8EAAE4
+ AZ4B1QH5AfYB/AH+Af8ByAHyAfwB/wG5Ae8B+wH/AawB7AH6Af8BnAHoAfkB/wGLAeMB9wH/AXsB4AH2
+ Af8BawHcAfYB/wFcAdkB9QH/AU4B1gH0Af8BQwHTAfMB/wHQAfMB/AH/ATcBnAHTAfcMAAFUAawBXgH/
+ AZQB0AGdAf8BkgHPAZoB/wFKAZ8BUwH/KAABZAGRAdUB2wF0Aa0C/wFYAZYB9AH2AwQBBgErAS8BNgE+
+ BAADgAGjAbkB0AL/AboB3AL/A3UBogQAAR8BIQElASwDBAEFAS4BdwHfAeQBMwGIAv8BNAF0Ac8B1QEl
+ AV4BjAHwAYsBxgHqAf8BYQGzAekB/wFXAaoB5gH/AVQBpgHkAf8BUQGiAeIB/wFOAZwB4AH/AUwBmAHd
+ Af8BSQGSAdsB/wFGAY0B2QH/AUMBiQHXAf8BQAGDAdUB/wE+AX4B0wH/ATsBegHQAf8BegGjAd4B/wEW
+ AVABfQH/BAABOAGkAdUB+gH+A/8B+AH9Av8B9gH9Av8B9QH8Av8B8wH8Af4B/wHYAfYB/AH/AZQB5gH4
+ Af8BhQHjAfcB/wF1Ad8B9gH/AWcB2wH1Af8BWwHYAfQB/wHXAfQB/AH/ATcBoQHTAfcMAAFXAbEBYgH/
+ AZgB0wGhAf8BlgHRAZ4B/wFqAbYBcwH/AUwBZgFPAY8MAAFDAVUBRAF7AwIBAxAAAXABqAH4AfkBnQHE
+ Av8BZAGjAv8DBwEJAVYBmAL/AVIBjwHmAeoBLAEvATQBPAPBAfsDuAH6ATEBNgE/AUgBOwF8AdwB4QEs
+ AX4C/wMHAQkBIwF5Av8BaQGmAv8BJgF6AfEB8wEmAV8BjgHwAY0ByAHtAf8BZAG4AesB/wFaAbIB6QH/
+ AVgBrgHnAf8BVgGpAeUB/wFTAaUB4wH/AVABoAHiAf8BTgGbAd4B/wFLAZYB3AH/AUgBkQHaAf8BRgGM
+ AdgB/wFCAYcB1gH/AT8BggHUAf8BfQGoAeAB/wEXAVEBfgH/BAABNgGoAdUB+gHoAfYB+wH/AZQB1AHv
+ Af8BiAHOAe4B/wFyAcEB6QH/AckB6QH2Af8B8gH8Af4B/wHzAfwB/gH/AfIB/AH+Af8B8AH8Af4B/wHv
+ AfsB/gH/Ae4B+wH+Af8B/gP/ATgBpwHTAfcMAAFaAYkBXwG3AYkBygGSAf8BkgHQAZsB/wGPAc0BlwH/
+ AVMBlgFaAeIBTAFmAU8BjwgAAUoBgQFQAdIBSQFoAUwBnxAAAXkBrAH4AfkBowHGAv8BbwGqAv8DAgED
+ AWIBnQH0AfYBZwGjAv8BLAEwATYEPwFOAyIBKgErAS8BNgE/AUoBkwL/ATYBgAH0AfYDAgEDAScBegL/
+ AWwBpQL/ASkBdwHtAfABJgFgAY8B8AGPAcwB7gH/AY8BzAH0Af8BhwHFAfIB/wGDAcMB8QH/AYABvwHv
+ Af8BfAG8Ae4B/wF5AbgB6wH/AXQBtAHqAf8BcAGwAegB/wFtAasB5gH/AWgBpwHlAf8BZQGjAeMB/wFh
+ AZ4B4QH/AYABrQHjAf8BFwFSAYEB/wQAAToBowHPAfIB8QH6Af0B/wGUAd4B9QH/AZMB3AH0Af8BgQHV
+ AfIB/wFpAcoB7QH/AWsBywHqAf8BhQHTAe8B/wGAAdIB7wH/AXkB0AHvAf8BdQHPAe4B/wFxAc8B7gH/
+ AekB9wH7Af8BOAGoAc8B8wwAAUYBVwFIAXABeAHEAYQB/wGdAdUBpgH/AZsB1AGjAf8BkQHOAZkB/wFs
+ AbgBdAH/AUwBoQFVAf8BSQGcAVEB/wFjAa4BawH/AWABqgFnAf8BRwFiAUsBkwwAAXsBngHSAdgBpAHG
+ Av8BeQGvAv8DBwEJAV8BeQGhAasBgQGzAv8BWAF0AaABqgEsATABNgE/ASwBMAE2AT8BTwFuAaABqgFl
+ AaMC/wFKAW0BoQGrAwcBCQEyAYIC/wFoAaQC/wE4AXIBzwHVASYBYQGQAfABdgGxAdkB/wEoAW0BpAH/
+ ARYBYAGbAf8BFgFdAZkB/wEWAVsBlgH/ARUBWgGSAf8BFAFYAY4B/wEUAVUBiwH/ARMBUwGHAf8BEwFQ
+ AYIB/wERAU0BfQH/AREBSwF5Af8BOAFnAYwB/wFmAY4BsAH/ARgBUwGCAf8EAAE6AaYBzAHwAfcB/AH+
+ Af8BjgHkAfgB/wGRAd4B9QH/AZ8B4AH1Af8BrAHhAfYB/wHvAfsB/gH/AfQB/QH+Af8B8wH8Af4B/wHx
+ AfwB/gH/Ae8B+wH+Af8B7gH7Af4B/wHzAfYB+AH5AUMBlgG0AdQMAAEWARcBFgEdAVoBhgFfAbABiAHL
+ AZEB/wGeAdYBpwH/AZQB0QGeAf8BmQHUAaIB/wGXAdIBoAH/AZQB0AGdAf8BkwHPAZsB/wGPAc0BlwH/
+ AWABqwFoAf8BRwFeAUkBiggAAWEBbwGFAZABngHEAv8BkAG+Av8BMAE0ATkBQgEVAhYBGwFtAZkB2AHe
+ AYMBtQL/AW8BqAL/AWkBpgL/AXABqAL/AVQBigHYAd4BFAEVARYBGwEsATEBOQFCAUwBkwL/AVsBnAL/
+ AUIBXAGBAY0BMAFhAYkB2wFAAXsBqgH/AWMBlgG+Af8BKgFuAaYB/wEfAWcBoQH/AR8BZAGfAf8BHwFj
+ AZwB/wEeAWEBmQH/AR4BXwGVAf8BHQFeAZEB/wEdAVwBjwH/ARwBWAGKAf8BPgFvAZgB/wFaAYQBpgH/
+ ATcBagGUAf8BOAFUAWwBrwQAATkBrwHUAfgB/QL+Af8B/gP/Av4C/wH9Af4C/wH+A/8B6gH3AfsB/wFq
+ AcIB3gH5AWoBwgHcAfgBagHCAdwB+AFqAcIB3AH4AXcBxwHeAfcBbwGzAcgB4QE1AUMBRwFWEAABKQEu
+ ASoBOQFaAYYBYAGwAXoBxgGFAf8BjAHNAZUB/wGXAdIBoAH/AZsB1AGkAf8BmQHTAaIB/wGWAdEBngH/
+ AZIBzwGaAf8BZAGvAW0B/wFHAVwBSQGECAABIwEkASYBLQGQAb0B+wH8AaYByAL/AYUBtQH7AfwBGAEZ
+ ARoBHwIJAQoBDAFUAWMBeQGEAWgBiwG+AcYBZAGHAb4BxgFPAWABeQGEAgkBCgEMARcBGAEaAR8BTAGR
+ AfsB/AFxAaoC/wE/AYgB+wH8AR4BIAEjASoBDQIOARIBKAFhAZEB7gE+AXkBqAH/AV8BkwG8Af8BYAGU
+ AbwB/wFgAZMBvAH/AV8BkgG7Af8BXwGRAbkB/wFfAZABtwH/AV8BjwG1Af8BXgGOAbMB/wFeAYwBsAH/
+ AVkBiAGrAf8BNwFsAZgB/wE7AUwBWAGDCAABQQGXAbAB0AFdAb4B3AH6AV8BvwHdAfoBXwG/Ad0B+gFf
+ Ab8B3QH6AV4BvwHdAfoBRQGhAb0B3QIRARIBFgELAgwBDwELAgwBDwELAgwBDwELAgwBDwELAgwBDwMD
+ AQQUAAEWARcBFgEdAUYBWAFIAXABWgGJAWEBtwFcAagBZAHqAVcBsQFhAf8BVAGsAV4B/wFvAboBdwH/
+ AWwBtgF1Af8BSwFmAU4BkBAAAWQBcAGFAZABpAHJAv8BpAHIAv8BiQG3AfsB/AExATQBOQFCAwcBCQgA
+ AwcBCQEvATMBOQFCAV0BnAH7AfwBdgGsAv8BaAGjAv8BSgFgAYEBjQgAAwkBDAE9AVkBbgGkASYBYgGR
+ AfEBJQFgAZIB8wElAWABkQHzASUBXwGQAfMBJQFfAY4B8wElAV4BjQHzASQBXQGMAfMBJAFdAYsB8wEj
+ AVwBiQHzASgBXAGFAecBOgFKAVUBfHAAAVcBkwFeAdIBUQFxAVQBnBQAAwQBBgF6AY4BrgG3AaQByAL/
+ AawBzgL/AZcBwAL/AYgBtgL/AYUBtwL/AXwBsAL/AXYBrgL/AX0BsQL/AY4BvgL/AXkBrwL/AVkBegGu
+ AbcDBAEGrAABSAFcAUsBeAMCAQMcAAFTAVoBZwFyAZMBtwH0AfYBqAHJAv8BsQHRAv8BswHRAv8BsAHQ
+ Av8BowHFAv8BlQHAAv8BcwGnAfQB9gFKAVYBZwFy3AACHwEhAScBYwFvAYEBjQGCAZsBxAHMAZMBvgH7
+ AfwBjgG6AfsB/AF6AZgBxAHMAV0BawGBAY0BHgEfASEBJxAAAUIBTQE+BwABPgMAASgDAAFAAwABYAMA
+ AQEBAAEBBgABAxYAA/+BAAH+AQcC/wH8AT8BhwH/AfwBBwHAAQMB8AEfAQMB/wHwAQcBgAEBAYABAwEB
+ Af8B4AEHAYABAQEAAQEBAAH/AcABBwGAAQEDAAF/AYABBwGAAQECAAGAAT8BAAEDAYABAQIAAcABPwEA
+ AQMBwAEDAgAB4AEfAQABAwHgAQcBAAEBAfABAQEAAQMB8AEPAQABAwH4AQABwAEDAfABBwEAAQ8B/gEA
+ AfABAwHwAQcBAAEPAf8BAAH4AQMB8AEPAQABDwH/AQgB/gEBAfABDwEAAQ8B/wEHAf8BAQH4AR8BAAEP
+ Af8BBwH/AfEC/wGAAR8B/wGHAf4BAwL/AfABAAL/AfwBAAL/AfABAAL/AfACAAEBAfABAAL/AYACAAEB
+ AfABAAL/AwABAQHAAQAC/wMAAQEBwAEAAYABAQMAAQEBwAEAAYABAQMAAQEBwAEAAYABAQMAAQEBAAED
+ AYABAQEAAQEBAAEBAQABAwGAAQEBAAEBAQABAQEAAQMBgAEBAQABAQEAAQEBAAEDAcABAwHwAQEBAAEB
+ AQABDwL/AfABAwEAAQEBAAEPAv8B8AEDAQABAQEAAQ8G/wEAAQ8C/wEPA/8BgAEBAYcB/wEHA/8BgAEB
+ AQMB/wEAAT8C/wGAAgEB/wEAAR8C/wGAAQEBAAH/AYABHwIAAcABAwEAAX8BwAEPAgAB4AEHAYABPwHA
+ AQMCAAHwAQ8BwAE/AcABAQIAAfwBPwHgAR8BwAEBAgAB/AE/AfABAQHAAQECAAHwAQ8B+AEAAeABAQIA
+ AeABBwH+AQAB+AEBAgABwAEDAf8BAAH8AQABgAEBAYABAQH/AQgB/AEAAYABAQGAAQEB/wEHAf4BAAHA
+ AQMBgAEBAf8BBwH/AfAC/wGAAQEB/wGHAYABAQH8AT8B/AE/AQ8B/wGAAQEB4AEHAeACBwH/AYABAQHA
+ AQMBwAEDAQABPwGAAQEBgAEBAcABAwEAAR8BgAEBAYABAQHAAQMBgAEfAYABAQHAAQMBwAEDAcABDwGA
+ AQECAAHAAQMBwAEDAYACAQGAAcABAwHAAQEBgAIBAYABwAEDAcABAQGAAQECAAHAAQMBwAEBAsMBwAED
+ AcABAwHgAQECwwGAAQEBwAEDAfgBAQHAAQMBgAEBAcABAwH8AQABwAEDAcABAwHAAQMB/AEAAeABBwHg
+ AQcB4AEHAf4BAAHwAQ8B/AE/A/8B8AT/AcMB/wHnAeMCAAL/AcMB/wHjAccCAAGAAQEBwwH/AfEBjwIA
+ AYABAQHDAf8B8AEPAgABgAEBAcMB/wH4AR8CAAGAAQEBwwH/ARgBHAIAAYABAQHDAf8BBAEgAgABgAEB
+ AcEBzwQAAYABAQHAAc8EAAGAAQEBwAEHBAABgAEBAcABAwQAAYABAQHgAQMDAAEBAYABAQHwAQcCgQGA
+ AQMD/wHPAYABAQX/Ac8B4AEHBv8B8AEPCw==
+
+
+
+
+
+ AAABAA8AMDAQAAEABABoBgAA9gAAACAgEAABAAQA6AIAAF4HAAAQEBAAAQAEACgBAABGCgAAAAAAAAEA
+ CABqDQAAbgsAADAwAAABAAgAqA4AANgYAAAgIAAAAQAIAKgIAACAJwAAEBAAAAEACABoBQAAKDAAAAAA
+ AAABABgAOQ0AAJA1AAAwMAAAAQAYAKgcAADJQgAAICAAAAEAGACoDAAAcV8AABAQAAABABgAaAMAABls
+ AAAAAAAAAQAgAHANAACBbwAAMDAAAAEAIACoJQAA8XwAACAgAAABACAAqBAAAJmiAAAQEAAAAQAgAGgE
+ AABBswAAKAAAADAAAABgAAAAAQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAIAAAACA
+ gACAAAAAgACAAICAAACAgIAAAAD/AAD/AAAA//8A/wAAAP8A/wD//wAAwMDAAP///wDwAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFRGVGVlZGVkdGVlAAAA
+ AAAAAAAAAAAAAFZGtkZLa2Rka0a0AAAAAAAAAAAAAAAAAGtka2tka2trZkZHAAAAAAAAAAAAAAAAAGRr
+ a2tmtmtra2tkAAAAAAAAAAAAAAAAAFZrZma2a2a2a2tnAAAAAAAAAAAAAAAAAEa2a2tra2tra2ZkAAAA
+ AAAAAAAAAAAAAGa2trZra2a2a2tlAAAAAAAAAAAAAAAAAHa2bbZrZttmtmtmAAAAAAABODE4ExgxAFa2
+ tra2tra2tmtlAAAAAAABgxg4E4OBAEZrZrZr1rZr29tmAAAAAAADgTETgxMTAHvWvb22tmvba2tlAAAA
+ AAADE4ODgxg4AGRr272729tr29vUAAAAAAAIE4MTgTgxAF2729vb22bb29tnAAAAAAABODg4ODgxAEbb
+ 29vb29u2vb22AAAAAAADg4ODg4ODAEZmZmZmZm1mZmZlAAAAAAABODg4ODg4AHR2VlZWR1ZHRlZWAAAA
+ AAAIODg4ODg4AAAAAAAAAAAAAAAAAAAAAAADg4ODg4ODM4ODiDiDg4ODg4OIOIODg44BODiDioOBiuiu
+ p6euinqK6K6np66o6j4BioODg4ODOurqjq6nrq6urqeup3qK6h4Dg4OKg4ODjoruqK6o6o6o6uqOqurq
+ 6o4Bg4ODiDg4Oq6orqeup66np6iuqOqOqD4Dg4qIOKg4h6eup66Kenp6eurqeup66j4Biog4qDiDPqen
+ p66urq6np66K6np6eo4DiKg4OKiBiq6nrqiuqKeup6p6enp66j4Bg4OIODgzOup66nrqeup6eurqenrq
+ eo4BMRMTgTGBh66orqenp6rorop66np66j4AAAAAAAAAOup66np66nrqrqrqenrqeo4AAAAAAAAAinp6
+ eup6enrqeurqeup66j4AAAAAAAAAPqrq6qeq6up6euqK6q6uqh4AAAAAAAAAiup6eup6eqeup66urqiu
+ 6j4AAAAAAAAAOup66np66n6qeqeqenrqqo4AAAAAAAAAGq6q6q6q6qrq6urqrqrq6j4AAAAAAAAAPq6u
+ qurq6urq6uqurq6q6o4AAAAAAAAAiq6q6uqq6q6qququqq6q6j4AAAAAAAAAOuqurqrq6uqurq6urq6u
+ ro4AAAAAAAAAiurqqurq6q6urqrqrqquqj4AAAAAAAAAPq6q6urqququqq6q6q6uro4AAAAAAAAAOqrq
+ 6qqurq6q6urqrq6q6j4AAAAAAAAAOurqqurqrqququqq6uquqj4AAAAAAAAAiuqurqrqrq6q6q6uqq6q
+ 6o4AAAAAAAAAOuququrqrqrq6q6q6uquqn4AAAAAAAAAeq6uququrqrqrq6q6q6uqo4AAAAAAAAAOq6q
+ rqquqq6qrqquqq6qrj4AAAAAAAAAgzODM4MzgzODM4MzgzODMT8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AA9///////4AAP///////wAA//8AAAP/AAD//wAAA/8AAP//AAAD/wAA//8AAAP/AAD//wAAA/8AAP//
+ AAAD/wAA//8AAAP/AAD//wAAA/8AAIADAAAD/wAAgAMAAAP/AACAAwAAA/8AAIADAAAD/wAAgAMAAAP/
+ AACAAwAAA/8AAIADAAAD/wAAgAMAAAP/AACAA/////8AAIAAAAAAAAAAgAAAAAAAAACAAAAAAAAAAIAA
+ AAAAAAAAgAAAAAAAAACAAAAAAAAAAIAAAAAAAAAAgAAAAAAAAACAAAAAAAAAAIAAAAAAAAAA//wAAAAA
+ AAD//AAAAAAAAP/8AAAAAAAA//wAAAAAAAD//AAAAAAAAP/8AAAAAAAA//wAAAAAAAD//AAAAAAAAP/8
+ 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
+ Hx8fHx8fHx8fGwAAARQXFxcXFxcXFxcXFxcXFxcXFxcXAQAAAAAAAAAAAAAAGyAhISEhISEhISEgGwAA
+ ARYZGRkZGRkZGRkZGRkZGRkZGRkZAQAAAAAAAAAAAAAAGyEhISEhISEhISEhGwAAARgaGhoaGhoaGhoa
+ GhoaGhoaGhoaAQAAAAAAAAAAAAAAGyIiIyIjIyIjIyMiGwAAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAA
+ AAAAAAAAAAAAGyUlJSUlJSUlJSUlGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGyYm
+ JiYmJiYmJiYmGyQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJEIAGyYnJygnKCcoJycnHCQv
+ Ly8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vJEEAGygpKSkpKSkpKSkpHCQwLzAwMDAwMDAwMDAw
+ MDAwMDAwMDAwMDAwMDAwMDAvJEEAGykrKiorKisqKyoqHCQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw
+ MDAwMDAwJEEAGyosLCwsLCwsLCwsGyQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwJEEAGywt
+ LS0tLS0tLS0tHCQxMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExJEEAGy0uLi4uLi4uLi4uHCQy
+ MjIxMjEyMTIxMjEyMTIxMjEyMTIxMjEyMTIxMjEyJEEAGy4uLi4uLi4uLi4uGyQyMjIyMjIyMjIyMjIy
+ MjIyMjIyMjIyMjIyMjIyMjIyJEEAGyorKysrKysrKysrGyQzMzQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0
+ NDQ0NDQzJEEAGxsbGxsbGxsbGxsbHCQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDU0JEEAAAAA
+ AAAAAAAAAAAAACQ1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1JEEAAAAAAAAAAAAAAAAAACQ1
+ NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1JEEAAAAAAAAAAAAAAAAAACQ2Njc3Nzc3Nzc3Nzc3
+ Nzc3Nzc3Nzc3Nzc3Nzc3NzY2JEEAAAAAAAAAAAAAAAAAACQ3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3
+ Nzc3Nzg3JEEAAAAAAAAAAAAAAAAAACQ4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4JEEAAAAA
+ AAAAAAAAAAAAACQ4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4JEEAAAAAAAAAAAAAAAAAACQ5
+ OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5JEEAAAAAAAAAAAAAAAAAACQ6Ojo6Ojo6Ojo6Ojo6
+ Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6JEEAAAAAAAAAAAAAAAAAACQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7
+ Ozs7Ozs7JEEAAAAAAAAAAAAAAAAAACQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7JEEAAAAA
+ AAAAAAAAAAAAACQ8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8JEEAAAAAAAAAAAAAAAAAACQ9
+ PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09JEEAAAAAAAAAAAAAAAAAACQ9PT09PT09PT09PT09
+ PT09PT09PT09PT09PT09PT09JEEAAAAAAAAAAAAAAAAAACQ+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+
+ Pj4+Pj4+JEEAAAAAAAAAAAAAAAAAACQ/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/JEEAAAAA
+ AAAAAAAAAAAAACQ/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/JEEAAAAAAAAAAAAAAAAAACRA
+ QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAJEEAAAAAAAAAAAAAAAAAACQkJCQkJCQkJCQkJCQk
+ JCQkJCQkJCQkJCQkJCQkJCQkJEMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAER///////4O7v///////w7u//8AAAP/Du7//wAAA/8O7v//AAAD/w7u//8AAAP/Du7//wAA
+ A/8O7v//AAAD/w7u//8AAAP/Du7//wAAA/8O7oADAAAD/w7ugAMAAAP/Du6AAwAAA/8O7oADAAAD/w7u
+ gAMAAAP/Du6AAwAAA/8O7oADAAAD/w7ugAMAAAP/Du6AA/////8O7oAAAAAAAA7ugAAAAAAADu6AAAAA
+ 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
+ HBwcHBwcHBwcHBwcHBwcHBwcEBktCxYWFhYWFgsQHR0dHR0dHR0dHR0dHR0dHR0dHR0QGS0LCwsLCwsL
+ CxAeHh4eHh4eHh4eHh4eHh4eHh4eHhAZAAAAAAAAAAAAEB8fHx8fHx8fHx8fHx8fHx8fHx8fEBkAAAAA
+ AAAAAAAQICAgICAgICAgICAgICAgICAgICAQGQAAAAAAAAAAABAhISEhISEhISEhISEhISEhISEhIRAZ
+ AAAAAAAAAAAAECIiIiIiIiIiIiIiIiIiIiIiIiIiEBkAAAAAAAAAAAAQIyMjIyMjIyMjIyMjIyMjIyMj
+ IyMQGQAAAAAAAAAAABAkJCQkJCQkJCQkJCQkJCQkJCQkJBAZAAAAAAAAAAAAECUkJSQlJCQkJCQkJCQk
+ JCQkJCQkEBkAAAAAAAAAAAAQJSUlJSUlJSUlJSUlJSUlJSUlJSUQGQAAAAAAAAAAABAmJiYmJiYmJiYm
+ JiYmJiYmJiYmJhAZAAAAAAAAAAAAECcnJycnJycnJycnJycnJycnJycnEBkAAAAAAAAAAAAQKCgoKCgo
+ KCgoKCgoKCgoKCgoKCgQGQAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQEBArAAAAAAAAAAAALy4u
+ Li4uLi4uLi4uLi4uLi4uLi4uLjD//////+AAf//gAD//4AA//+AAP//gAD//4AA/AGAAPwAgAD8AIAA/
+ ACAAPwAgAD8AP///AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/4AAAP+AAAD/gAAA/4AAAP+AAAD/gAAA
+ /4AAAP+AAAD/gAAA/4AAAP+AAAD/gAAA/4AAACgAAAAQAAAAIAAAAAEACAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAoj8AAK5OAAC6XQAAr2MPAMZtAACqbEwAsHNMALZ7TACwflQAvINMALKDbwC2h28A
+ uYxvAL2RbwC2jnQAADerAAA8tgAQUL8AQGi9AABCwgAAR80AEFPEAABM2AAlW8AAQGrDAD6w3wA+tuEA
+ PrzkAHiGwgB4iMUAcIzKAHiJyQB4jM0AZI3QAHCT2QBftdwAX7neAGCt2ABgstoAR7zmAF+/4QA9wucA
+ PcjqADzO7QA81O8AO9ryAEfD6QBGyuwAXsLhAEbR7wBG2PIARd/1AEXl+ABE7PwAu7zZALu93ACBuuAA
+ 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
+ ADAAAABgAAAAAQAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7i4gAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7g
+ 4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAGYzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2Yz
+ M2YzM2YzM2YzM2YzM2YzM2YzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM5o5A546AJ46AJ46AJ46AJ46AJ46
+ AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AGYzMwAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AGYzM54+A6I/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/
+ AKI/AGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM6JCA6ZDAKZDAKZDAKZDAKZDAKZDAKZDAKZDAKZDAKZD
+ AKZDAKZDAKZDAKZDAKZDAKZDAKZDAKZDAKZDAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM6ZHA6pJAKpJ
+ AKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAGYzMwAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAGYzM6lMA65OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5O
+ AK5OAK5OAK5OAK5OAK5OAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM61RA7JTALJTALJTALJTALJTALJT
+ ALJTALJTALJTALJTALJTALJTALJTALJTALJTALJTALJTALJTALJTAGYzMwAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AGYzM7FWA7ZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZ
+ ALZZAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQAzoQAzoQAzoQAzoQAzoQAz
+ oQAzoQAzoQAzoQAzoQAzoQAzoQAAAAAAAGYzM7RaA7pdALpdALpdALpdALpdALpdALpdALpdALpdALpd
+ ALpdALpdALpdALpdALpdALpdALpdALpdALpdAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAzoQA0owA0pAA0pAA0pAA0pAA0pAA0pAA0pAA0pAA0pAA0pAAzoQAAAAAAAGYzM7hfA75iAL5i
+ AL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAGYzMwAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA2pwA2qAA2qAA2qAA2qAA2qAA2qAA2qAA2qAA2
+ qAA2qAAzoQAAAAAAAGYzM7xkA8JoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJo
+ AMJoAMJoAMJoAMJoAMJoAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA3qwA3
+ qwA3qwA3qwA3qwA3qwA3qwA3qwA3qwA3qwA3qwAzoQAAAAAAAGYzM8BpA8ZtAMZtAMZtAMZtAMZtAMZt
+ AMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAGYzMwAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAzoQA5rgA5rwA5rwA5rwA5rwA5rwA5rwA5rwA5rwA5rwA5rwAzoQAAAAAA
+ AGYzM8NuA8pyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpy
+ AMpyAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA6sQA7swA7swA7swA7swA7
+ swA7swA7swA7swA7swA7swAzoQAAAAAAAGYzM8dzA853AM53AM53AM53AM53AM53AM53AM53AM53AM53
+ AM53AM53AM53AM53AM53AM53AM53AM53AM53AGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAzoQA8tQA8tgA8tgA8tgA8tgA8tgA8tgA8tgA8tgA8tgA8tgAzoQAAAAAAAGYzM8t3A9J8ANJ8
+ ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8AGYzMwAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA9uAA+ugA+ugA+ugA+ugA+ugA+ugA+ugA+ugA+
+ ugA+ugAzoQAAAAAAAGYzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2Yz
+ M2YzM2YzM2YzM2YzM2YzM2YzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA/vABA
+ vgBAvgBAvgBAvgBAvgBAvgBAvgBAvgBAvgBAvgAzoQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAzoQBBvwBBwQBBwQBBwQBBwQBBwQBBwQBBwQBBwQBBwQBBwQAzoQBbrwBb
+ rwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBb
+ rwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbr7rH3wAAAAAzoQBCwwBDxQBDxQBDxQBDxQBD
+ xQBDxQBDxQBDxQBDxQBDxQAzoQBbr0i45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei4
+ 5Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45ABbr6y8
+ 2QAAAAAzoQBExgBFyQBFyQBFyQBFyQBFyQBFyQBFyQBFyQBFyQBFyQAzoQBbr0i65Ui65Ui65Ui65Ui6
+ 5Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui6
+ 5Ui65Ui65Ui65Ui65Ui65Ui65QBbr6y82QAAAAAzoQBFygBHzABHzABHzABHzABHzABHzABHzABHzABH
+ zABHzAAzoQBbr0i85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki8
+ 5ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85gBbr6y82QAAAAAzoQBHzQBI
+ 0ABI0ABI0ABI0ABI0ABI0ABI0ABI0ABI0ABI0AAzoQBbr0i/50i/50i/50i/50i/50i/50i/50i/50i/
+ 50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/
+ 50i/50i/5wBbr6y82QAAAAAzoQBI0QBK1ABK1ABK1ABK1ABK1ABK1ABK1ABK1ABK1ABK1AAzoQBbr0fB
+ 6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB
+ 6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6ABbr6y82QAAAAAzoQBK1ABM2ABM2ABM2ABM2ABM
+ 2ABM2ABM2ABM2ABM2ABM2AAzoQBbr0fD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD
+ 6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6QBbr6y8
+ 2QAAAAAzoQBM2ABN2wBN2wBN2wBN2wBN2wBN2wBN2wBN2wBN2wBN2wAzoQBbr0fF6kfF6kfF6kfF6kfF
+ 6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF
+ 6kfF6kfF6kfF6kfF6kfF6kfF6gBbr6y82QAAAAAzoQBGzABIzwBIzwBIzwBIzwBIzwBIzwBIzwBIzwBI
+ zwBIzwAzoQBbr0fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI
+ 60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI6wBbr6y82QAAAAAzoQAzoQAz
+ oQAzoQAzoQAzoQAzoQAzoQAzoQAzoQAzoQAzoQAzoQBbr0fK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK
+ 7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK
+ 7EfK7EfK7ABbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0fM
+ 7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM
+ 7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7QBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAABbr0bO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO
+ 7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7gBbr6y8
+ 2QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0bR70bR70bR70bR70bR
+ 70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR
+ 70bR70bR70bR70bR70bR70bR7wBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAABbr0bT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT
+ 8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8ABbr6y82QAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0bV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV
+ 8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV
+ 8kbV8kbV8gBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0bX
+ 80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX
+ 80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX8wBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAABbr0ba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba
+ 9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9ABbr6y8
+ 2QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xc9UXc9UXc9UXc9UXc
+ 9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc
+ 9UXc9UXc9UXc9UXc9UXc9UXc9QBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAABbr0Xe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe
+ 9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9gBbr6y82QAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg
+ 90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg
+ 90Xg90Xg9wBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xj
+ +EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj
+ +EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+ABbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl
+ +UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+QBbr6y8
+ 2QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xn+kXn+kXn+kXn+kXn
+ +kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn
+ +kXn+kXn+kXn+kXn+kXn+kXn+gBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAABbr0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp
+ +0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+wBbr6y82QAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Ts/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs
+ /ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs
+ /ETs/ETs/ABbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Tu
+ /UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu
+ /UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/QBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAABbr0Tw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw
+ /kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/gBbr669
+ 2QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbrwBbrwBbrwBbrwBbrwBb
+ rwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBb
+ rwBbrwBbrwBbrwBbrwBbrwBbrwBbr+XZ4gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7i4n///////g7u////
+ ////Du7//wAAA/8O7v//AAAD/w7u//8AAAP/Du7//wAAA/8O7v//AAAD/w7u//8AAAP/Du7//wAAA/8O
+ 7v//AAAD/w7ugAMAAAP/Du6AAwAAA/8O7oADAAAD/w7ugAMAAAP/Du6AAwAAA/8O7oADAAAD/w7ugAMA
+ AAP/Du6AAwAAA/8O7oAD/////w7ugAAAAAAADu6AAAAAAAAO7oAAAAAAAA7ugAAAAAAADu6AAAAAAAAO
+ 7oAAAAAAAA7ugAAAAAAADu6AAAAAAAAO7oAAAAAAAA7ugAAAAAAADu7//AAAAAAO7v/8AAAAAA7u//wA
+ AAAADu7//AAAAAAO7v/8AAAAAA7u//wAAAAADu7//AAAAAAO7v/8AAAAAA7u//wAAAAADu7//AAAAAAO
+ 7v/8AAAAAA7u//wAAAAADu7//AAAAAAO7v/8AAAAAA7u//wAAAAADu7//AAAAAAO7v/8AAAAAA7u//wA
+ AAAADu7///////4O7igAAAAgAAAAQAAAAAEAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AABmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzMAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmMzOfOwCfOwCfOwCfOwCfOwCfOwCf
+ OwCfOwCfOwCfOwCfOwCfOwBmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAABmMzOkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQBmMzOxmJgA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmMzOrSgCrSgCr
+ SgCrSgCrSgCrSgCrSgCrSgCrSgCrSgCrSgCrSgBmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmMzOwUQCwUQCwUQCwUQCwUQCwUQCwUQCwUQCwUQCwUQCw
+ UQCwUQBmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AABmMzO3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgBmMzOxmJgAAAAAAAAAAAAAAAAA
+ AAAAAADHu9QAM6EAM6EAM6EAM6EAM6EAM6EAM6EAM6EAAAAAAABmMzO8YAC8YAC8YAC8YAC8YAC8YAC8
+ YAC8YAC8YAC8YAC8YAC8YABmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAADHu9QAM6EANqkANqkANqkANqkA
+ NqkANqkAM6F/mM8AAABmMzPDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQBmMzOxmJgA
+ AAAAAAAAAAAAAAAAAAAAAADHu9QAM6EAOK0AOK0AOK0AOK0AOK0AOK0AM6F/mM8AAABmMzPIcADIcADI
+ cADIcADIcADIcADIcADIcADIcADIcADIcADIcABmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAADHu9QAM6EA
+ O7QAO7QAO7QAO7QAO7QAO7QAM6F/mM8AAABmMzPPeADPeADPeADPeADPeADPeADPeADPeADPeADPeADP
+ eADPeABmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAADHu9QAM6EAPbgAPbgAPbgAPbgAPbgAPbgAM6F/mM8A
+ AABmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzOxmJgAAAAAAAAAAAAAAAAA
+ AAAAAADHu9QAM6EAQL8AQL8AQL8AQL8AQL8AQL8AM6FVfMEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADHu9QAM6EAQsMAQsMAQsMAQsMA
+ QsMAQsMAM6EAW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68A
+ W68AW68AW68AW68AW6+BpM7Hu9QAM6EARcoARcoARcoARcoARcoARcoAM6EAW69IuuVIuuVIuuVIuuVI
+ uuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuUAW698oMzHu9QAM6EA
+ R84AR84AR84AR84AR84AR84AM6EAW69IvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZI
+ vuZIvuZIvuZIvuZIvuZIvuZIvuZIvuYAW698oMzHu9QAM6EAStUAStUAStUAStUAStUAStUAM6EAW69H
+ wehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwegA
+ 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
+ SH08XNsEAFaMAdioj++z4Eh9MkLbBABWjAHYsLfvs+BIzR6pbQIAK8YAbEAAYqPmEACUwBiAFr18nwVH
+ au4obRMAWDEGYP2evs+CI/XpaG0TAFgxBmC9Hr7PgiM1b4y2CQCsGAOwLgGIjfqMAKAExgCss6fvs+BI
+ fT5W2wQAVowBaL6H77PgSH0xTtsEAFaMAWjW3fdZcKS+HK9tAgArxgA07e77LDhS88drmwDAijEAa3fz
+ fRYcqQVV2iYAsGIMQJOuvs+CI/XVBG0TAFgxBmCtLr7PgiP19URtEwBYIQDlgQCgJMYArNnZ91lwpL6Z
+ pG0CACvGAKzRyfdZcKQWTtY2AYAVYwBW7+j7LDhSi6q1TQBgxRiA1QhAbNRiAoASGAPQuIPvs+BILZmi
+ bQIAK8YArNre91lwpL6t0TYBgBVjAFZp5/ssOFLfTdU2AYAVYwBWJgCxUd8TAJTAGIBGbX2fBUfqh2na
+ JgCwYgzASm18nwVHaul0bRMAWDEFAPEjALBCAMoTAYAVAlCeCACsEIDyRAAARI8AAAkjAEDCCACQMAIA
+ JIwAAAkjAEDCCACQMFMAAKRlbv1PNdVF4Jm637at+5rUfY3qvrL4iScADfsZgOX03tj+IOMAAAAASUVO
+ RK5CYIIoAAAAMAAAAGAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/svLi/7Ly2D+y8tb/svL
+ W/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svL
+ W/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svL
+ W/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8uU/svL
+ ev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AGUyMhxlMjIgZTIyIGUyMiBlMjIgZTIyIGUyMiBlMjIgZTIyIGUyMiBlMjIgZTIyIGUyMiBlMjIgZTIy
+ IGUyMiBlMjIgZTIyIGUyMiBlMjIgZTIyIGUyMiBlMjIIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAGYzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz
+ /2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+aOQP/njoA/546AP+eOgD/njoA/546AP+eOgD/njoA
+ /546AP+eOgD/njoA/546AP+eOgD/njoA/546AP+eOgD/njoA/546AP+eOgD/njoA/2YzM/9lMjJAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+ePgP/oj8A/6I/AP+iPwD/oj8A
+ /6I/AP+iPwD/oj8A/6I/AP+iPwD/oj8A/6I/AP+iPwD/oj8A/6I/AP+iPwD/oj8A/6I/AP+iPwD/oj8A
+ /2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+iQgP/pkMA
+ /6ZDAP+mQwD/pkMA/6ZDAP+mQwD/pkMA/6ZDAP+mQwD/pkMA/6ZDAP+mQwD/pkMA/6ZDAP+mQwD/pkMA
+ /6ZDAP+mQwD/pkMA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svL
+ ev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AGYzM/+mRwP/qkkA/6pJAP+qSQD/qkkA/6pJAP+qSQD/qkkA/6pJAP+qSQD/qkkA/6pJAP+qSQD/qkkA
+ /6pJAP+qSQD/qkkA/6pJAP+qSQD/qkkA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAGYzM/+pTAP/rk4A/65OAP+uTgD/rk4A/65OAP+uTgD/rk4A/65OAP+uTgD/rk4A
+ /65OAP+uTgD/rk4A/65OAP+uTgD/rk4A/65OAP+uTgD/rk4A/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+tUQP/slMA/7JTAP+yUwD/slMA/7JTAP+yUwD/slMA
+ /7JTAP+yUwD/slMA/7JTAP+yUwD/slMA/7JTAP+yUwD/slMA/7JTAP+yUwD/slMA/2YzM/9lMjJAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+xVgP/tlkA/7ZZAP+2WQD/tlkA
+ /7ZZAP+2WQD/tlkA/7ZZAP+2WQD/tlkA/7ZZAP+2WQD/tlkA/7ZZAP+2WQD/tlkA/7ZZAP+2WQD/tlkA
+ /2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AM6H/ADOh
+ /wAzof8AM6H/ADOh/wAzof8AM6H/ADOh/wAzof8AM6H/ADOh/wAzof8AMqAsAAAAAGYzM/+0WgP/ul0A
+ /7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A
+ /7pdAP+6XQD/ul0A/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svL
+ egAzof8ANKP/ADSk/wA0pP8ANKT/ADSk/wA0pP8ANKT/ADSk/wA0pP8ANKT/ADSk/wAzof8AMqBAAAAA
+ AGYzM/+4XwP/vmIA/75iAP++YgD/vmIA/75iAP++YgD/vmIA/75iAP++YgD/vmIA/75iAP++YgD/vmIA
+ /75iAP++YgD/vmIA/75iAP++YgD/vmIA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAD+y8tb/svLegAzof8ANqf/ADao/wA2qP8ANqj/ADao/wA2qP8ANqj/ADao/wA2qP8ANqj/ADao
+ /wAzof8AMqBAAAAAAGYzM/+8ZAP/wmgA/8JoAP/CaAD/wmgA/8JoAP/CaAD/wmgA/8JoAP/CaAD/wmgA
+ /8JoAP/CaAD/wmgA/8JoAP/CaAD/wmgA/8JoAP/CaAD/wmgA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AN6v/ADer/wA3q/8AN6v/ADer/wA3q/8AN6v/ADer
+ /wA3q/8AN6v/ADer/wAzof8AMqBAAAAAAGYzM//AaQP/xm0A/8ZtAP/GbQD/xm0A/8ZtAP/GbQD/xm0A
+ /8ZtAP/GbQD/xm0A/8ZtAP/GbQD/xm0A/8ZtAP/GbQD/xm0A/8ZtAP/GbQD/xm0A/2YzM/9lMjJAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AOa7/ADmv/wA5r/8AOa//ADmv
+ /wA5r/8AOa//ADmv/wA5r/8AOa//ADmv/wAzof8AMqBAAAAAAGYzM//DbgP/ynIA/8pyAP/KcgD/ynIA
+ /8pyAP/KcgD/ynIA/8pyAP/KcgD/ynIA/8pyAP/KcgD/ynIA/8pyAP/KcgD/ynIA/8pyAP/KcgD/ynIA
+ /2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AOrH/ADuz
+ /wA7s/8AO7P/ADuz/wA7s/8AO7P/ADuz/wA7s/8AO7P/ADuz/wAzof8AMqBAAAAAAGYzM//HcwP/zncA
+ /853AP/OdwD/zncA/853AP/OdwD/zncA/853AP/OdwD/zncA/853AP/OdwD/zncA/853AP/OdwD/zncA
+ /853AP/OdwD/zncA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svL
+ egAzof8APLX/ADy2/wA8tv8APLb/ADy2/wA8tv8APLb/ADy2/wA8tv8APLb/ADy2/wAzof8AMqBAAAAA
+ AGYzM//LdwP/0nwA/9J8AP/SfAD/0nwA/9J8AP/SfAD/0nwA/9J8AP/SfAD/0nwA/9J8AP/SfAD/0nwA
+ /9J8AP/SfAD/0nwA/9J8AP/SfAD/0nwA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAD+y8tb/svLegAzof8APbj/AD66/wA+uv8APrr/AD66/wA+uv8APrr/AD66/wA+uv8APrr/AD66
+ /wAzof8AMqBAAAAAAGYzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz
+ /2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AP7z/AEC+/wBAvv8AQL7/AEC+/wBAvv8AQL7/AEC+
+ /wBAvv8AQL7/AEC+/wAzof8AMqBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AQb//AEHB/wBBwf8AQcH/AEHB
+ /wBBwf8AQcH/AEHB/wBBwf8AQcH/AEHB/wAzof8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv
+ /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv
+ /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/+InsiS/svLegAzof8AQsP/AEPF
+ /wBDxf8AQ8X/AEPF/wBDxf8AQ8X/AEPF/wBDxf8AQ8X/AEPF/wAzof8AW6//SLjk/0i45P9IuOT/SLjk
+ /0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk
+ /0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/wBbr/9+mMWk/svL
+ egAzof8ARMb/AEXJ/wBFyf8ARcn/AEXJ/wBFyf8ARcn/AEXJ/wBFyf8ARcn/AEXJ/wAzof8AW6//SLrl
+ /0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl
+ /0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl
+ /wBbr/9+mMWk/svLegAzof8ARcr/AEfM/wBHzP8AR8z/AEfM/wBHzP8AR8z/AEfM/wBHzP8AR8z/AEfM
+ /wAzof8AW6//SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm
+ /0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm
+ /0i85v9IvOb/SLzm/wBbr/9+mMWk/svLegAzof8AR83/AEjQ/wBI0P8ASND/AEjQ/wBI0P8ASND/AEjQ
+ /wBI0P8ASND/AEjQ/wAzof8AW6//SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n
+ /0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n
+ /0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/wBbr/9+mMWk/svLegAzof8ASNH/AErU/wBK1P8AStT/AErU
+ /wBK1P8AStT/AErU/wBK1P8AStT/AErU/wAzof8AW6//R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho
+ /0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho
+ /0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/wBbr/9+mMWk/8zMegAzof8AStT/AEzY
+ /wBM2P8ATNj/AEzY/wBM2P8ATNj/AEzY/wBM2P8ATNj/AEzY/wAzof8AW6//R8Pp/0fD6f9Hw+n/R8Pp
+ /0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp
+ /0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/wBbr/9+mMWk/8zM
+ egAzof8ATNj/AE3b/wBN2/8ATdv/AE3b/wBN2/8ATdv/AE3b/wBN2/8ATdv/AE3b/wAzof8AW6//R8Xq
+ /0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq
+ /0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq
+ /wBbr/9+mMWk/8zMegAzof8ARsz/AEjP/wBIz/8ASM//AEjP/wBIz/8ASM//AEjP/wBIz/8ASM//AEjP
+ /wAzof8AW6//R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr
+ /0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr
+ /0fI6/9HyOv/R8jr/wBbr/9+mMWk/8zMegAzof8AM6H/ADOh/wAzof8AM6H/ADOh/wAzof8AM6H/ADOh
+ /wAzof8AM6H/ADOh/wAzof8AW6//R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs
+ /0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs
+ /0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt
+ /0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt
+ /0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rs7u/0bO7v9Gzu7/Rs7u
+ /0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u
+ /0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/wBbr/9+mMWk/8zM
+ ev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//RtHv
+ /0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv
+ /0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv
+ /wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAW6//RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw
+ /0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw
+ /0bT8P9G0/D/RtPw/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAW6//RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy
+ /0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy
+ /0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz
+ /0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz
+ /0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rtr0/0ba9P9G2vT/Rtr0
+ /0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0
+ /0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/wBbr/9+mMWk/svL
+ ev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rdz1
+ /0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1
+ /0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1
+ /wBbr/9+mMWk/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAW6//Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72
+ /0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72
+ /0Xe9v9F3vb/Rd72/wBbr/9+mMWk/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAW6//ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3
+ /0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3
+ /0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/wBbr/9+mMWk/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4
+ /0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4
+ /0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/wBbr/9+mMWk/svLev7LywgAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//ReX5/0Xl+f9F5fn/ReX5
+ /0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5
+ /0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/wBbr/9+mMWk/svL
+ ev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Ref6
+ /0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6
+ /0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6
+ /wBbr/9+mMWk/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAW6//ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7
+ /0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7
+ /0Tp+/9E6fv/ROn7/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAW6//ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8
+ /0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8
+ /0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/wBbr/9+mMWk/svLev/LywgAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79
+ /0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79
+ /0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/wBbr/9+mMWk/svLev7LywgAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//RPD+/0Tw/v9E8P7/RPD+
+ /0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+
+ /0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/wBbr/+BmMSi/svL
+ ev7Lyw7+y8sI/8zMCP/MzAj+y8sI/svLCP7Lywj+y8sI/svLCP7Lywj+y8sI/8zMCP/MzAgAW6//AFuv
+ /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv
+ /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv
+ /wBbr//NtcaA/svLfP/Ly3r+y8t6/8zMev/MzHr+y8t6/svLev7Ly3r+y8t6/svLev7Ly3r+y8t6/8zM
+ ev/MzHr/zMx6/8zMev/MzHr/zMx6/8zMev/MzHr/zMx6/8zMev/MzHr+y8t6/svLev7Ly3r+y8t6/svL
+ ev7Ly3r+y8t6/svLev7Ly3r+y8t6/svLev7Ly3r+y8t6/svLev7Ly3r+y8t6/svLev7Ly3r+y8t6/svL
+ ev7Ly3r+y8t6/svLev7Ly3r+y8uLAAAAAAAADu4//wAAAf4O7j//AAAB/g7uP/8AAAH+Du4//wAAAf4O
+ 7j//AAAB/g7uP/8AAAH+Du4//wAAAf4O7j//AAAB/g7uP/8AAAH+Du4AAQAAAf4O7gABAAAB/g7uAAEA
+ AAH+Du4AAQAAAf4O7gABAAAB/g7uAAEAAAH+Du4AAQAAAf4O7gABAAAB/g7uAAH////+Du4AAAAAAAAO
+ 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
+ PamYvaYAM6H/AEC//wBAv/8AQL//AEC//wBAv/8AQL//ADOh/wA7o6oAW69VAFuvVQBbr1UAW69VAFuv
+ VQBbr1UAW69VAFuvVQBbr1UAW69VAFuvVQBbr1UAW69VAFuvVQBbr1UAW69VAFuvVQBbr1UAW69VAFuv
+ VQBbr1WsqMRlqZi9pgAzof8AQsP/AELD/wBCw/8AQsP/AELD/wBCw/8AM6H/AFuv/wBbr/8AW6//AFuv
+ /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv
+ /wBbr/8AW6//AFuv/1aFvr6pmL2mADOh/wBFyv8ARcr/AEXK/wBFyv8ARcr/AEXK/wAzof8AW6//SLrl
+ /0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl
+ /0i65f9IuuX/SLrl/0i65f8AW6//VIO9wqmYvaYAM6H/AEfO/wBHzv8AR87/AEfO/wBHzv8AR87/ADOh
+ /wBbr/9Ivub/SL7m/0i+5v9Ivub/SL7m/0i+5v9Ivub/SL7m/0i+5v9Ivub/SL7m/0i+5v9Ivub/SL7m
+ /0i+5v9Ivub/SL7m/0i+5v9Ivub/SL7m/wBbr/9Ug73CqZi9pgAzof8AStX/AErV/wBK1f8AStX/AErV
+ /wBK1f8AM6H/AFuv/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho
+ /0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/AFuv/1SDvcKqmb2mADOh/wBM2v8ATNr/AEza
+ /wBM2v8ATNr/AEza/wAzof8AW6//R8Tp/0fE6f9HxOn/R8Tp/0fE6f9HxOn/R8Tp/0fE6f9HxOn/R8Tp
+ /0fE6f9HxOn/R8Tp/0fE6f9HxOn/R8Tp/0fE6f9HxOn/R8Tp/0fE6f8AW6//VIO9wqqZvaYAM6H/ADOh
+ /wAzof8AM6H/ADOh/wAzof8AM6H/ADOh/wBbr/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr
+ /0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/wBbr/9Ug73C4rvH
+ b15rsFcAM6FVADOhVQAzoVUAM6FVADOhVQAzoVUAM6FVAFuv/0fL7P9Hy+z/R8vs/0fL7P9Hy+z/R8vs
+ /0fL7P9Hy+z/R8vs/0fL7P9Hy+z/R8vs/0fL7P9Hy+z/R8vs/0fL7P9Hy+z/R8vs/0fL7P9Hy+z/AFuv
+ /1SDvcL/zMxU/8zMAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rs/u/0bP7v9Gz+7/Rs/u
+ /0bP7v9Gz+7/Rs/u/0bP7v9Gz+7/Rs/u/0bP7v9Gz+7/Rs/u/0bP7v9Gz+7/Rs/u/0bP7v9Gz+7/Rs/u
+ /0bP7v8AW6//VIO9wv/MzFT/zMwDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr/9G0u//RtLv
+ /0bS7/9G0u//RtLv/0bS7/9G0u//RtLv/0bS7/9G0u//RtLv/0bS7/9G0u//RtLv/0bS7/9G0u//RtLv
+ /0bS7/9G0u//RtLv/wBbr/9Ug73C/8zMVP/MzAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFuv
+ /0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy
+ /0bV8v9G1fL/RtXy/0bV8v9G1fL/AFuv/1SDvcL/zMxU/8zMAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAW6//Rtnz/0bZ8/9G2fP/Rtnz/0bZ8/9G2fP/Rtnz/0bZ8/9G2fP/Rtnz/0bZ8/9G2fP/Rtnz
+ /0bZ8/9G2fP/Rtnz/0bZ8/9G2fP/Rtnz/0bZ8/8AW6//VIO9wv7Ly1T+y8sDAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAABbr/9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1
+ /0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/wBbr/9Ug73C/svLVP7LywMAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAFuv/0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/Rd/2
+ /0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/AFuv/1SDvcL+y8tU/svL
+ AwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4
+ /0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P8AW6//VIO9
+ wv7Ly1T+y8sDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr/9F5vn/Reb5/0Xm+f9F5vn/Reb5
+ /0Xm+f9F5vn/Reb5/0Xm+f9F5vn/Reb5/0Xm+f9F5vn/Reb5/0Xm+f9F5vn/Reb5/0Xm+f9F5vn/Reb5
+ /wBbr/9Ug73C/svLVP7LywMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFuv/0Tq+/9E6vv/ROr7
+ /0Tq+/9E6vv/ROr7/0Tq+/9E6vv/ROr7/0Tq+/9E6vv/ROr7/0Tq+/9E6vv/ROr7/0Tq+/9E6vv/ROr7
+ /0Tq+/9E6vv/AFuv/1SDvcL+y8tU/8vLAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//RO38
+ /0Tt/P9E7fz/RO38/0Tt/P9E7fz/RO38/0Tt/P9E7fz/RO38/0Tt/P9E7fz/RO38/0Tt/P9E7fz/RO38
+ /0Tt/P9E7fz/RO38/0Tt/P8AW6//VIO9wv7Ly1X+y8sF/8zMA/7LywP+y8sD/svLA/7LywP+y8sD/8zM
+ AwBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv
+ /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/9mir25/svLb/7Ly1X/zMxU/svLVP7Ly1T+y8tU/svL
+ VP7Ly1T/zMxUxrLFi6qmwqaqpsKmqqbCpqqmwqaqpsKmqaXBpqmlwaappcGmqaXBpqmlwaappcGmqaXB
+ pqmlwaappcGmqaXBpqmlwaappcGmqaXBpqmlwaappcGmqaXBpta5xpIAAAAAP8AAPj/AAD4/wAA+P8AA
+ Pj/AAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAAAAAAAAAAAACgAAAAQAAAAIAAA
+ AAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tB/svLHv7Lyx7+y8se/svLHp1qanCYZWWjmGVl
+ o5hlZaOYZWWjmGVlo5hlZaOYZWV4/svLHv7Lyx7+y8s5/svLLAAAAAAAAAAAAAAAAAAAAABmMzP/ZjMz
+ /2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/wAAAAAAAAAA/svLHv7LyywAAAAAAAAAAAAAAAAAAAAAZjMz
+ /61NAP+tTQD/rU0A/61NAP+tTQD/rU0A/2YzM/8AAAAAAAAAAP7Lyx4AM6H/ADOh/wAzof8AM6H/ADOh
+ /2YzM/+5XQD/uV0A/7ldAP+5XQD/uV0A/7ldAP9mMzP/AAAAAAAAAAD+y8seADOh/wA3q/8AN6v/ADer
+ /wAzof9mMzP/xWwA/8VsAP/FbAD/xWwA/8VsAP/FbAD/ZjMz/wAAAAAAAAAA/svLHgAzof8APLb/ADy2
+ /wA8tv8AM6H/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/8AAAAAAAAAAP7Lyx4AM6H/AEHB
+ /wBBwf8AQcH/ADOh/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//ADOh
+ /wBGzP8ARsz/AEbM/wAzof9IvOX/SLzl/0i85f9IvOX/SLzl/0i85f9IvOX/SLzl/0i85f9IvOX/AFuv
+ /wAzof8AS9f/AEvX/wBL1/8AM6H/R8Lo/0fC6P9Hwuj/R8Lo/0fC6P9Hwuj/R8Lo/0fC6P9Hwuj/R8Lo
+ /wBbr/8AM6H/ADOh/wAzof8AM6H/ADOh/0fJ6/9Hyev/R8nr/0fJ6/9Hyev/R8nr/0fJ6/9Hyev/R8nr
+ /0fJ6/8AW6///8zMLAAAAAAAAAAAAAAAAABbr/9G0O7/RtDu/0bQ7v9G0O7/RtDu/0bQ7v9G0O7/RtDu
+ /0bQ7v9G0O7/AFuv///MzCwAAAAAAAAAAAAAAAAAW6//Rtfy/0bX8v9G1/L/Rtfy/0bX8v9G1/L/Rtfy
+ /0bX8v9G1/L/Rtfy/wBbr//+y8ssAAAAAAAAAAAAAAAAAFuv/0Xd9f9F3fX/Rd31/0Xd9f9F3fX/Rd31
+ /0Xd9f9F3fX/Rd31/0Xd9f8AW6///svLLAAAAAAAAAAAAAAAAABbr/9F5Pj/ReT4/0Xk+P9F5Pj/ReT4
+ /0Xk+P9F5Pj/ReT4/0Xk+P9F5Pj/AFuv//7LyywAAAAAAAAAAAAAAAAAW6//ROv7/0Tr+/9E6/v/ROv7
+ /0Tr+/9E6/v/ROv7/0Tr+/9E6/v/ROv7/wBbr//+y8tI/svLLP7Lyyz+y8ssAFuv/wBbr/8AW6//AFuv
+ /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AACsQXgGrEF4BqxBAAasQQAGrEEABqxBAACs
+ QQAArEEAAKxBAACsQXAArEFwAKxBcACsQXAArEFwAKxBAACsQQ==
+
+
+
\ No newline at end of file
diff --git a/branches/ph-plugins/ExtraTools/NtObjects/Program.cs b/branches/ph-plugins/ExtraTools/NtObjects/Program.cs
new file mode 100644
index 000000000..da2872772
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/NtObjects/Program.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Windows.Forms;
+
+namespace NtObjects
+{
+ static class Program
+ {
+ ///
+ /// The main entry point for the application.
+ ///
+ [STAThread]
+ static void Main()
+ {
+ Application.EnableVisualStyles();
+ Application.SetCompatibleTextRenderingDefault(false);
+ Application.Run(new ObjectsWindow());
+ }
+ }
+}
diff --git a/branches/ph-plugins/ExtraTools/NtObjects/Properties/AssemblyInfo.cs b/branches/ph-plugins/ExtraTools/NtObjects/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..c34b08eca
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/NtObjects/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("NtObjects")]
+[assembly: AssemblyDescription("NtObjects")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("wj32")]
+[assembly: AssemblyProduct("NtObjects")]
+[assembly: AssemblyCopyright("Copyright © 2009 wj32. Licensed under the GNU GPL, v3.")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("5cea10c2-8c9e-45c7-be29-93a5ec8e3f30")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/branches/ph-plugins/ExtraTools/NtObjects/Properties/Resources.Designer.cs b/branches/ph-plugins/ExtraTools/NtObjects/Properties/Resources.Designer.cs
new file mode 100644
index 000000000..152144e10
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/NtObjects/Properties/Resources.Designer.cs
@@ -0,0 +1,71 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:2.0.50727.3074
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace NtObjects.Properties
+{
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources
+ {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources()
+ {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager
+ {
+ get
+ {
+ if ((resourceMan == null))
+ {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("NtObjects.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture
+ {
+ get
+ {
+ return resourceCulture;
+ }
+ set
+ {
+ resourceCulture = value;
+ }
+ }
+ }
+}
diff --git a/branches/ph-plugins/ExtraTools/NtObjects/Properties/Resources.resx b/branches/ph-plugins/ExtraTools/NtObjects/Properties/Resources.resx
new file mode 100644
index 000000000..ffecec851
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/NtObjects/Properties/Resources.resx
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/branches/ph-plugins/ExtraTools/NtObjects/Properties/Settings.Designer.cs b/branches/ph-plugins/ExtraTools/NtObjects/Properties/Settings.Designer.cs
new file mode 100644
index 000000000..e906f1ebc
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/NtObjects/Properties/Settings.Designer.cs
@@ -0,0 +1,30 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:2.0.50727.3074
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace NtObjects.Properties
+{
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "9.0.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
+ {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default
+ {
+ get
+ {
+ return defaultInstance;
+ }
+ }
+ }
+}
diff --git a/branches/ph-plugins/ExtraTools/NtObjects/Properties/Settings.settings b/branches/ph-plugins/ExtraTools/NtObjects/Properties/Settings.settings
new file mode 100644
index 000000000..abf36c5d3
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/NtObjects/Properties/Settings.settings
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/branches/ph-plugins/ExtraTools/NtObjects/app.config b/branches/ph-plugins/ExtraTools/NtObjects/app.config
new file mode 100644
index 000000000..b7db28170
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/NtObjects/app.config
@@ -0,0 +1,3 @@
+
+
+
diff --git a/branches/ph-plugins/ExtraTools/NtProfiler/NtProfiler.csproj b/branches/ph-plugins/ExtraTools/NtProfiler/NtProfiler.csproj
new file mode 100644
index 000000000..f156eeca4
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/NtProfiler/NtProfiler.csproj
@@ -0,0 +1,130 @@
+
+
+
+ Debug
+ AnyCPU
+ 9.0.30729
+ 2.0
+ {E3CEB6D7-7080-4089-B54E-41025E30CE46}
+ WinExe
+ Properties
+ NtProfiler
+ NtProfiler
+ v2.0
+ 512
+
+
+ NtProfiler.Program
+ publish\
+ true
+ Disk
+ false
+ Foreground
+ 7
+ Days
+ false
+ false
+ true
+ 0
+ 1.0.0.%2a
+ false
+ false
+ true
+ app.manifest
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ AnyCPU
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ AnyCPU
+
+
+
+
+
+
+
+
+
+
+
+ Form
+
+
+ ProfilerWindow.cs
+
+
+
+
+ ProfilerWindow.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+ Designer
+
+
+ True
+ Resources.resx
+
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+ True
+ Settings.settings
+ True
+
+
+
+
+ False
+ .NET Framework 2.0 %28AnyCPU%29
+ true
+
+
+ False
+ .NET Framework 3.0 %28AnyCPU%29
+ false
+
+
+ False
+ .NET Framework 3.5
+ false
+
+
+
+
+ {8E10F5E8-D4FA-4980-BB23-2EDD134AC15E}
+ ProcessHacker.Common
+
+
+ {8A448157-E1A7-4DDF-954E-287F1117832B}
+ ProcessHacker.Native
+
+
+
+
+
\ No newline at end of file
diff --git a/branches/ph-plugins/ExtraTools/NtProfiler/ProfilerWindow.Designer.cs b/branches/ph-plugins/ExtraTools/NtProfiler/ProfilerWindow.Designer.cs
new file mode 100644
index 000000000..b8e5f9bf4
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/NtProfiler/ProfilerWindow.Designer.cs
@@ -0,0 +1,304 @@
+namespace NtProfiler
+{
+ partial class ProfilerWindow
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ProfilerWindow));
+ this.menuStripMain = new System.Windows.Forms.MenuStrip();
+ this.profilerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.profileProcessToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.profileKernelToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.tabControl = new System.Windows.Forms.TabControl();
+ this.tabModules = new System.Windows.Forms.TabPage();
+ this.listModules = new System.Windows.Forms.ListView();
+ this.columnName = new System.Windows.Forms.ColumnHeader();
+ this.columnHits = new System.Windows.Forms.ColumnHeader();
+ this.columnFileName = new System.Windows.Forms.ColumnHeader();
+ this.tabFunctions = new System.Windows.Forms.TabPage();
+ this.toolStripContainer = new System.Windows.Forms.ToolStripContainer();
+ this.toolStripProfileControl = new System.Windows.Forms.ToolStrip();
+ this.toolStripButtonStart = new System.Windows.Forms.ToolStripButton();
+ this.toolStripButtonStop = new System.Windows.Forms.ToolStripButton();
+ this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator();
+ this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.listFunctions = new System.Windows.Forms.ListView();
+ this.columnFunction = new System.Windows.Forms.ColumnHeader();
+ this.columnFunctionHits = new System.Windows.Forms.ColumnHeader();
+ this.menuStripMain.SuspendLayout();
+ this.tabControl.SuspendLayout();
+ this.tabModules.SuspendLayout();
+ this.tabFunctions.SuspendLayout();
+ this.toolStripContainer.ContentPanel.SuspendLayout();
+ this.toolStripContainer.TopToolStripPanel.SuspendLayout();
+ this.toolStripContainer.SuspendLayout();
+ this.toolStripProfileControl.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // menuStripMain
+ //
+ this.menuStripMain.Dock = System.Windows.Forms.DockStyle.None;
+ this.menuStripMain.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.profilerToolStripMenuItem});
+ this.menuStripMain.Location = new System.Drawing.Point(0, 0);
+ this.menuStripMain.Name = "menuStripMain";
+ this.menuStripMain.Size = new System.Drawing.Size(661, 24);
+ this.menuStripMain.TabIndex = 0;
+ this.menuStripMain.Text = "menuStrip1";
+ //
+ // profilerToolStripMenuItem
+ //
+ this.profilerToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.profileProcessToolStripMenuItem,
+ this.profileKernelToolStripMenuItem,
+ this.toolStripMenuItem1,
+ this.exitToolStripMenuItem});
+ this.profilerToolStripMenuItem.Name = "profilerToolStripMenuItem";
+ this.profilerToolStripMenuItem.ShowShortcutKeys = false;
+ this.profilerToolStripMenuItem.Size = new System.Drawing.Size(57, 20);
+ this.profilerToolStripMenuItem.Text = "&Profiler";
+ //
+ // profileProcessToolStripMenuItem
+ //
+ this.profileProcessToolStripMenuItem.Name = "profileProcessToolStripMenuItem";
+ this.profileProcessToolStripMenuItem.Size = new System.Drawing.Size(160, 22);
+ this.profileProcessToolStripMenuItem.Text = "Profile &Process...";
+ this.profileProcessToolStripMenuItem.Click += new System.EventHandler(this.profileProcessToolStripMenuItem_Click);
+ //
+ // profileKernelToolStripMenuItem
+ //
+ this.profileKernelToolStripMenuItem.Name = "profileKernelToolStripMenuItem";
+ this.profileKernelToolStripMenuItem.Size = new System.Drawing.Size(160, 22);
+ this.profileKernelToolStripMenuItem.Text = "Profile &Kernel";
+ this.profileKernelToolStripMenuItem.Click += new System.EventHandler(this.profileKernelToolStripMenuItem_Click);
+ //
+ // tabControl
+ //
+ this.tabControl.Controls.Add(this.tabModules);
+ this.tabControl.Controls.Add(this.tabFunctions);
+ this.tabControl.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.tabControl.Location = new System.Drawing.Point(0, 0);
+ this.tabControl.Name = "tabControl";
+ this.tabControl.SelectedIndex = 0;
+ this.tabControl.Size = new System.Drawing.Size(661, 404);
+ this.tabControl.TabIndex = 1;
+ //
+ // tabModules
+ //
+ this.tabModules.Controls.Add(this.listModules);
+ this.tabModules.Location = new System.Drawing.Point(4, 22);
+ this.tabModules.Name = "tabModules";
+ this.tabModules.Padding = new System.Windows.Forms.Padding(3);
+ this.tabModules.Size = new System.Drawing.Size(653, 378);
+ this.tabModules.TabIndex = 0;
+ this.tabModules.Text = "Modules";
+ this.tabModules.UseVisualStyleBackColor = true;
+ //
+ // listModules
+ //
+ this.listModules.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
+ this.columnName,
+ this.columnHits,
+ this.columnFileName});
+ this.listModules.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.listModules.FullRowSelect = true;
+ this.listModules.HideSelection = false;
+ this.listModules.Location = new System.Drawing.Point(3, 3);
+ this.listModules.Name = "listModules";
+ this.listModules.ShowItemToolTips = true;
+ this.listModules.Size = new System.Drawing.Size(647, 372);
+ this.listModules.TabIndex = 0;
+ this.listModules.UseCompatibleStateImageBehavior = false;
+ this.listModules.View = System.Windows.Forms.View.Details;
+ this.listModules.DoubleClick += new System.EventHandler(this.listModules_DoubleClick);
+ //
+ // columnName
+ //
+ this.columnName.Text = "Name";
+ this.columnName.Width = 160;
+ //
+ // columnHits
+ //
+ this.columnHits.Text = "Hits";
+ this.columnHits.Width = 100;
+ //
+ // columnFileName
+ //
+ this.columnFileName.Text = "File Name";
+ this.columnFileName.Width = 300;
+ //
+ // tabFunctions
+ //
+ this.tabFunctions.Controls.Add(this.listFunctions);
+ this.tabFunctions.Location = new System.Drawing.Point(4, 22);
+ this.tabFunctions.Name = "tabFunctions";
+ this.tabFunctions.Padding = new System.Windows.Forms.Padding(3);
+ this.tabFunctions.Size = new System.Drawing.Size(653, 378);
+ this.tabFunctions.TabIndex = 1;
+ this.tabFunctions.Text = "Functions";
+ this.tabFunctions.UseVisualStyleBackColor = true;
+ //
+ // toolStripContainer
+ //
+ //
+ // toolStripContainer.ContentPanel
+ //
+ this.toolStripContainer.ContentPanel.Controls.Add(this.tabControl);
+ this.toolStripContainer.ContentPanel.Size = new System.Drawing.Size(661, 404);
+ this.toolStripContainer.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.toolStripContainer.Location = new System.Drawing.Point(0, 0);
+ this.toolStripContainer.Name = "toolStripContainer";
+ this.toolStripContainer.Size = new System.Drawing.Size(661, 453);
+ this.toolStripContainer.TabIndex = 1;
+ this.toolStripContainer.Text = "toolStripContainer1";
+ //
+ // toolStripContainer.TopToolStripPanel
+ //
+ this.toolStripContainer.TopToolStripPanel.Controls.Add(this.menuStripMain);
+ this.toolStripContainer.TopToolStripPanel.Controls.Add(this.toolStripProfileControl);
+ //
+ // toolStripProfileControl
+ //
+ this.toolStripProfileControl.Dock = System.Windows.Forms.DockStyle.None;
+ this.toolStripProfileControl.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.toolStripButtonStart,
+ this.toolStripButtonStop});
+ this.toolStripProfileControl.Location = new System.Drawing.Point(3, 24);
+ this.toolStripProfileControl.Name = "toolStripProfileControl";
+ this.toolStripProfileControl.Size = new System.Drawing.Size(58, 25);
+ this.toolStripProfileControl.TabIndex = 1;
+ //
+ // toolStripButtonStart
+ //
+ this.toolStripButtonStart.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+ this.toolStripButtonStart.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButtonStart.Image")));
+ this.toolStripButtonStart.ImageTransparentColor = System.Drawing.Color.Magenta;
+ this.toolStripButtonStart.Name = "toolStripButtonStart";
+ this.toolStripButtonStart.Size = new System.Drawing.Size(23, 22);
+ this.toolStripButtonStart.Text = "Start";
+ this.toolStripButtonStart.Click += new System.EventHandler(this.toolStripButtonStart_Click);
+ //
+ // toolStripButtonStop
+ //
+ this.toolStripButtonStop.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+ this.toolStripButtonStop.Enabled = false;
+ this.toolStripButtonStop.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButtonStop.Image")));
+ this.toolStripButtonStop.ImageTransparentColor = System.Drawing.Color.Magenta;
+ this.toolStripButtonStop.Name = "toolStripButtonStop";
+ this.toolStripButtonStop.Size = new System.Drawing.Size(23, 22);
+ this.toolStripButtonStop.Text = "Stop";
+ this.toolStripButtonStop.Click += new System.EventHandler(this.toolStripButtonStop_Click);
+ //
+ // toolStripMenuItem1
+ //
+ this.toolStripMenuItem1.Name = "toolStripMenuItem1";
+ this.toolStripMenuItem1.Size = new System.Drawing.Size(157, 6);
+ //
+ // exitToolStripMenuItem
+ //
+ this.exitToolStripMenuItem.Name = "exitToolStripMenuItem";
+ this.exitToolStripMenuItem.Size = new System.Drawing.Size(160, 22);
+ this.exitToolStripMenuItem.Text = "E&xit";
+ this.exitToolStripMenuItem.Click += new System.EventHandler(this.exitToolStripMenuItem_Click);
+ //
+ // listFunctions
+ //
+ this.listFunctions.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
+ this.columnFunction,
+ this.columnFunctionHits});
+ this.listFunctions.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.listFunctions.FullRowSelect = true;
+ this.listFunctions.HideSelection = false;
+ this.listFunctions.Location = new System.Drawing.Point(3, 3);
+ this.listFunctions.Name = "listFunctions";
+ this.listFunctions.ShowItemToolTips = true;
+ this.listFunctions.Size = new System.Drawing.Size(647, 372);
+ this.listFunctions.TabIndex = 1;
+ this.listFunctions.UseCompatibleStateImageBehavior = false;
+ this.listFunctions.View = System.Windows.Forms.View.Details;
+ //
+ // columnFunction
+ //
+ this.columnFunction.Text = "Function";
+ this.columnFunction.Width = 300;
+ //
+ // columnFunctionHits
+ //
+ this.columnFunctionHits.Text = "Hits";
+ this.columnFunctionHits.Width = 100;
+ //
+ // ProfilerWindow
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(661, 453);
+ this.Controls.Add(this.toolStripContainer);
+ this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
+ this.MainMenuStrip = this.menuStripMain;
+ this.Name = "ProfilerWindow";
+ this.Text = "NtProfiler";
+ this.menuStripMain.ResumeLayout(false);
+ this.menuStripMain.PerformLayout();
+ this.tabControl.ResumeLayout(false);
+ this.tabModules.ResumeLayout(false);
+ this.tabFunctions.ResumeLayout(false);
+ this.toolStripContainer.ContentPanel.ResumeLayout(false);
+ this.toolStripContainer.TopToolStripPanel.ResumeLayout(false);
+ this.toolStripContainer.TopToolStripPanel.PerformLayout();
+ this.toolStripContainer.ResumeLayout(false);
+ this.toolStripContainer.PerformLayout();
+ this.toolStripProfileControl.ResumeLayout(false);
+ this.toolStripProfileControl.PerformLayout();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.MenuStrip menuStripMain;
+ private System.Windows.Forms.ToolStripMenuItem profilerToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem profileProcessToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem profileKernelToolStripMenuItem;
+ private System.Windows.Forms.TabControl tabControl;
+ private System.Windows.Forms.TabPage tabModules;
+ private System.Windows.Forms.TabPage tabFunctions;
+ private System.Windows.Forms.ListView listModules;
+ private System.Windows.Forms.ColumnHeader columnName;
+ private System.Windows.Forms.ColumnHeader columnHits;
+ private System.Windows.Forms.ColumnHeader columnFileName;
+ private System.Windows.Forms.ToolStripContainer toolStripContainer;
+ private System.Windows.Forms.ToolStrip toolStripProfileControl;
+ private System.Windows.Forms.ToolStripButton toolStripButtonStart;
+ private System.Windows.Forms.ToolStripButton toolStripButtonStop;
+ private System.Windows.Forms.ToolStripSeparator toolStripMenuItem1;
+ private System.Windows.Forms.ToolStripMenuItem exitToolStripMenuItem;
+ private System.Windows.Forms.ListView listFunctions;
+ private System.Windows.Forms.ColumnHeader columnFunction;
+ private System.Windows.Forms.ColumnHeader columnFunctionHits;
+ }
+}
+
diff --git a/branches/ph-plugins/ExtraTools/NtProfiler/ProfilerWindow.cs b/branches/ph-plugins/ExtraTools/NtProfiler/ProfilerWindow.cs
new file mode 100644
index 000000000..1a11d43a7
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/NtProfiler/ProfilerWindow.cs
@@ -0,0 +1,267 @@
+using System;
+using System.Collections.Generic;
+using System.Windows.Forms;
+using ProcessHacker.Common.Ui;
+using ProcessHacker.Native;
+using ProcessHacker.Native.Api;
+using ProcessHacker.Native.Objects;
+using ProcessHacker.Native.Security;
+using ProcessHacker.Native.Symbols;
+
+namespace NtProfiler
+{
+ public partial class ProfilerWindow : Form
+ {
+ private readonly IntPtr _userModeBase;
+ private readonly IntPtr _userModeLimit;
+ private readonly IntPtr _kernelModeBase;
+ private readonly IntPtr _kernelModeLimit;
+
+ private ProfileHandle _profileHandle;
+ private Dictionary _kernelModules;
+ private SymbolProvider _kernelSymbols;
+ private IntPtr _profileBase;
+ private uint _profileSize;
+ private int _bucketSizeLog;
+ private uint _bucketSize;
+
+ public ProfilerWindow()
+ {
+ InitializeComponent();
+
+ unchecked
+ {
+ _userModeBase = new IntPtr(0x00000000);
+ _userModeLimit = new IntPtr(0x7fffffff);
+ _kernelModeBase = new IntPtr((int)0x80000000);
+ _kernelModeLimit = new IntPtr((int)0xffffffff);
+ }
+
+ try { KProcessHacker.Instance = new KProcessHacker(); }
+ catch { }
+
+ try
+ {
+ using (var thandle = ProcessHandle.GetCurrent().GetToken(TokenAccess.Query | TokenAccess.AdjustPrivileges))
+ thandle.SetPrivilege("SeSystemProfilePrivilege", SePrivilegeAttributes.Enabled);
+ }
+ catch
+ { }
+
+ Win32.LoadLibrary("C:\\Program Files\\Debugging Tools for Windows (x86)\\dbghelp.dll");
+ SymbolProvider.Options |= SymbolOptions.DeferredLoads;
+
+ listModules.ListViewItemSorter = new SortedListViewComparer(listModules)
+ {
+ SortColumn = 1,
+ SortOrder = SortOrder.Descending
+ };
+ listFunctions.ListViewItemSorter = new SortedListViewComparer(listFunctions)
+ {
+ SortColumn = 1,
+ SortOrder = SortOrder.Descending
+ };
+ }
+
+ private uint GetKernelModeCodeRange(out IntPtr baseAddress)
+ {
+ IntPtr minAddress = _kernelModeLimit;
+ IntPtr maxAddress = _kernelModeBase;
+
+ foreach (var module in Windows.GetKernelModules())
+ {
+ if (module.BaseAddress.CompareTo(_kernelModeBase) == -1)
+ continue;
+
+ if (module.BaseAddress.CompareTo(minAddress) == -1)
+ minAddress = module.BaseAddress;
+ if (module.BaseAddress.CompareTo(maxAddress) == 1)
+ maxAddress = module.BaseAddress;
+ }
+
+ baseAddress = minAddress;
+
+ return maxAddress.Decrement(minAddress).ToUInt32();
+ }
+
+ private IntPtr GetAddress(int bufferIndex)
+ {
+ return _profileBase.Increment(_bucketSize * bufferIndex);
+ }
+
+ private void LoadKernelSymbols()
+ {
+ _kernelSymbols = new SymbolProvider(new ProcessHandle(4, ProcessAccess.QueryInformation));
+ _kernelSymbols.PreloadModules = true;
+
+ foreach (var module in Windows.GetKernelModules())
+ {
+ try
+ {
+ _kernelSymbols.LoadModule(module.FileName, module.BaseAddress);
+ }
+ catch
+ { }
+ }
+ }
+
+ private void LoadProfileModules()
+ {
+ int[] counters = _profileHandle.Collect();
+ Dictionary modules = new Dictionary();
+
+ for (int i = 0; i < counters.Length; i++)
+ {
+ if (counters[i] != 0)
+ {
+ IntPtr realAddress = this.GetAddress(i);
+ IntPtr baseAddress;
+
+ _kernelSymbols.GetModuleFromAddress(realAddress, out baseAddress);
+
+ if (!modules.ContainsKey(baseAddress))
+ modules.Add(baseAddress, 0);
+
+ modules[baseAddress]++;
+ }
+ }
+
+ listModules.Items.Clear();
+
+ foreach (var moduleBase in modules.Keys)
+ {
+ listModules.Items.Add(new ListViewItem(
+ new string[]
+ {
+ _kernelModules[moduleBase].BaseName,
+ modules[moduleBase].ToString("N0"),
+ _kernelModules[moduleBase].FileName
+ })
+ {
+ Tag = moduleBase
+ }
+ );
+ }
+ }
+
+ private void LoadProfileFunctions(IntPtr moduleBase)
+ {
+ int[] counters = _profileHandle.Collect();
+ Dictionary functions = new Dictionary();
+
+ for (int i = 0; i < counters.Length; i++)
+ {
+ if (counters[i] != 0)
+ {
+ IntPtr realAddress = this.GetAddress(i);
+ IntPtr baseAddress;
+
+ _kernelSymbols.GetModuleFromAddress(realAddress, out baseAddress);
+
+ if (baseAddress != moduleBase)
+ continue;
+
+ string fileName;
+ string symbolName;
+ ulong displacement;
+
+ symbolName = _kernelSymbols.GetSymbolFromAddress(realAddress.ToUInt64(), out fileName, out displacement);
+
+ if (symbolName != null)
+ {
+ if (!functions.ContainsKey(symbolName))
+ functions.Add(symbolName, 0);
+
+ functions[symbolName]++;
+ }
+ }
+ }
+
+ listFunctions.Items.Clear();
+
+ foreach (var function in functions.Keys)
+ {
+ listFunctions.Items.Add(new ListViewItem(
+ new string[]
+ {
+ function,
+ functions[function].ToString("N0")
+ }));
+ }
+ }
+
+ #region Menu Items
+
+ #region Profiler
+
+ private void profileProcessToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+
+ }
+
+ private void profileKernelToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ IntPtr baseAddress;
+ uint size = this.GetKernelModeCodeRange(out baseAddress);
+
+ _kernelModules = new Dictionary();
+
+ foreach (var module in Windows.GetKernelModules())
+ _kernelModules.Add(module.BaseAddress, module);
+
+ _profileBase = baseAddress;
+ _profileSize = size;
+ _bucketSizeLog = 6; // 64 byte bucket size
+ _bucketSize = (uint)(2 << (_bucketSizeLog - 1));
+ _profileHandle = ProfileHandle.Create(
+ null,
+ baseAddress,
+ size,
+ _bucketSizeLog,
+ KProfileSource.ProfileTime,
+ IntPtr.Zero
+ );
+ ProfileHandle.SetInterval(KProfileSource.ProfileTime, 1); // 100 nanoseconds
+
+ this.LoadKernelSymbols();
+ }
+
+ private void exitToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ this.Close();
+ }
+
+ #endregion
+
+ #endregion
+
+ #region Toolbar
+
+ #region Profile Control
+
+ private void toolStripButtonStart_Click(object sender, EventArgs e)
+ {
+ _profileHandle.Start();
+ toolStripButtonStart.Enabled = false;
+ toolStripButtonStop.Enabled = true;
+ }
+
+ private void toolStripButtonStop_Click(object sender, EventArgs e)
+ {
+ _profileHandle.Stop();
+ toolStripButtonStart.Enabled = true;
+ toolStripButtonStop.Enabled = false;
+ this.LoadProfileModules();
+ }
+
+ #endregion
+
+ #endregion
+
+ private void listModules_DoubleClick(object sender, EventArgs e)
+ {
+ this.LoadProfileFunctions((IntPtr)listModules.SelectedItems[0].Tag);
+ tabControl.SelectedTab = tabFunctions;
+ }
+ }
+}
diff --git a/branches/ph-plugins/ExtraTools/NtProfiler/ProfilerWindow.resx b/branches/ph-plugins/ExtraTools/NtProfiler/ProfilerWindow.resx
new file mode 100644
index 000000000..fad04cebc
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/NtProfiler/ProfilerWindow.resx
@@ -0,0 +1,944 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 17, 17
+
+
+ 125, 17
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+ YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAdhJREFUOE+tU8mq
+ IjEUfR/lP/kB1frKXtUPiKCCijtBEVy4cCUIBhVBHHCK4sKF81hqOaCoeF9OeFUiiIumA5eCSs5w70m+
+ vv736na7NlGaKNbpdHTOud5qtVij0dCq1arto16v17MLIF+tVnQ6neh+v8s6Ho80n8+pUqnwcrlsf0sC
+ sFA0LpcLYeG73+/JMAw6n8/yH0hLpZJRLBZfSX5tcxO83W5ps9lQPp8nXddpvV4TXJkk2WyWi3q2g57N
+ AyYYQKfzm3K5nAQvFgtZWP1+n1KplGa10m63GezBKoCmqsPhJBRjTIIxB8wD7hKJBLMIms2mjmHtdjsJ
+ huJyuRRghyyX6y+JBGg6nUqi6/VK0WhUtwhERJIA9k0wDgKsqi5SlD9ybzKZWC4ikciToF6vM0wcbZi9
+ wq6qqi/g8XgskxkMBhQOh58tCHsaNh+Ph1SYzWbSrqIoUhl7o9GIhsMh3W43EjFSKBR6DlFkaysUChwz
+ wIJVFMAmEKoQAIkA82Aw+HorM5mMPZ1OG8gcBw+Hg+UGlwnKIBFgw+v1vr+NyWTSHo/Hea1Wky1gJugZ
+ uSNKn8/HPR7Pe7AZSSwWs4kBaYFAgPn9fl2o6QLE3G63Jgg+P6Z/edk/yU9ET/seY4MAAAAASUVORK5C
+ YII=
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+ YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAcNJREFUOE+tU0ur
+ QVEYvT/KfzLCXzgYS6GQmSJlYGCklD2QkkdemwwMvJ8bxyNCvrvX7u6jW+4d3O6ulXLOenzr2+fj479P
+ r9ezSRgSrNvtCs65aLfbrNlsGrVazfarX7/ft0siX6/XdD6f6fF4KJxOJ1osFlStVnmlUrG/FQFZOprX
+ 65Vw8Hs4HMg0TbpcLuo/iJbLZbNUKn0X+YrNNXm329F2uyUhhMJmsyGk0iKFQoFLvMbBzPqFn8jL5ZIA
+ nMFgQNls1rBG6XQ6DPEQVbu6XC4CnE4neb1eRUYP6APp0uk0swRarZZAWfv9XgkgDYgaEJjP5zSbzZTQ
+ 7XajRCIhLAG5IiWA+CCvViuL7HA4yOPx0HQ6VdAp4vH4S6DRaDA0jjH0rHAFEXC73Yo8mUzUZobDIcVi
+ sdcI8oIYePh8PpWDjqtd8Ww8HtNoNKL7/U5yjRSNRl8lyt3aisUiRwc474hwhQFEJJlHIpHvtzKfz9tz
+ uZyJnePF4/FopcFlgjNEJNkMBALvb2Mmk7GnUiler9dV4+gEM2PvjDEKBoPc7/e/J+uVJJNJmyzICIfD
+ LBQKCekmJIn5fD5DCvz+Mf3ly/4E08xAcyCRlTQAAAAASUVORK5CYII=
+
+
+
+
+ AAABAA8AMDAQAAEABABoBgAA9gAAACAgEAABAAQA6AIAAF4HAAAQEBAAAQAEACgBAABGCgAAAAAAAAEA
+ CABqDQAAbgsAADAwAAABAAgAqA4AANgYAAAgIAAAAQAIAKgIAACAJwAAEBAAAAEACABoBQAAKDAAAAAA
+ AAABABgAOQ0AAJA1AAAwMAAAAQAYAKgcAADJQgAAICAAAAEAGACoDAAAcV8AABAQAAABABgAaAMAABls
+ AAAAAAAAAQAgAHANAACBbwAAMDAAAAEAIACoJQAA8XwAACAgAAABACAAqBAAAJmiAAAQEAAAAQAgAGgE
+ AABBswAAKAAAADAAAABgAAAAAQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAIAAAACA
+ gACAAAAAgACAAICAAACAgIAAAAD/AAD/AAAA//8A/wAAAP8A/wD//wAAwMDAAP///wDwAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFRGVGVlZGVkdGVlAAAA
+ AAAAAAAAAAAAAFZGtkZLa2Rka0a0AAAAAAAAAAAAAAAAAGtka2tka2trZkZHAAAAAAAAAAAAAAAAAGRr
+ a2tmtmtra2tkAAAAAAAAAAAAAAAAAFZrZma2a2a2a2tnAAAAAAAAAAAAAAAAAEa2a2tra2tra2ZkAAAA
+ AAAAAAAAAAAAAGa2trZra2a2a2tlAAAAAAAAAAAAAAAAAHa2bbZrZttmtmtmAAAAAAABODE4ExgxAFa2
+ tra2tra2tmtlAAAAAAABgxg4E4OBAEZrZrZr1rZr29tmAAAAAAADgTETgxMTAHvWvb22tmvba2tlAAAA
+ AAADE4ODgxg4AGRr272729tr29vUAAAAAAAIE4MTgTgxAF2729vb22bb29tnAAAAAAABODg4ODgxAEbb
+ 29vb29u2vb22AAAAAAADg4ODg4ODAEZmZmZmZm1mZmZlAAAAAAABODg4ODg4AHR2VlZWR1ZHRlZWAAAA
+ AAAIODg4ODg4AAAAAAAAAAAAAAAAAAAAAAADg4ODg4ODM4ODiDiDg4ODg4OIOIODg44BODiDioOBiuiu
+ p6euinqK6K6np66o6j4BioODg4ODOurqjq6nrq6urqeup3qK6h4Dg4OKg4ODjoruqK6o6o6o6uqOqurq
+ 6o4Bg4ODiDg4Oq6orqeup66np6iuqOqOqD4Dg4qIOKg4h6eup66Kenp6eurqeup66j4Biog4qDiDPqen
+ p66urq6np66K6np6eo4DiKg4OKiBiq6nrqiuqKeup6p6enp66j4Bg4OIODgzOup66nrqeup6eurqenrq
+ eo4BMRMTgTGBh66orqenp6rorop66np66j4AAAAAAAAAOup66np66nrqrqrqenrqeo4AAAAAAAAAinp6
+ eup6enrqeurqeup66j4AAAAAAAAAPqrq6qeq6up6euqK6q6uqh4AAAAAAAAAiup6eup6eqeup66urqiu
+ 6j4AAAAAAAAAOup66np66n6qeqeqenrqqo4AAAAAAAAAGq6q6q6q6qrq6urqrqrq6j4AAAAAAAAAPq6u
+ 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
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQIDAwMDAwMDAwMDAwMDAwMDAwMDAQAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAQQFBQUFBQUFBQUFBQUFBQUFBQUFAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AQYHBwcHBwcHBwcHBwcHBwcHBwcHAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQgJCQkJCQkJCQkJ
+ CQkJCQkJCQkJAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQoMlZWVlZWVlZWVlZWVlZWVlZWVAQAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQwNDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NAQAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAQ4PEA8PDw8PDw8PDw8PDw8PDw8PAQAAAAAAAAAAAAAAGxsbGxsbGxsbGxsbHAAA
+ AQ8QEBAQEBAQEBAQEBAQEBAQEBAQAQAAAAAAAAAAAAAAGxwdHR0dHR0dHR0dGwAAARASERERERERERER
+ ERERERERERERAQAAAAAAAAAAAAAAHB4eHh4eHh4eHh4eGwAAARITExMTExMTExMTExMTExMTExMTAQAA
+ AAAAAAAAAAAAGx4eHh8eHx4fHh8fGwAAARMVFRUVFRUVFRUVFRUVFRUVFRUVAQAAAAAAAAAAAAAAGx8f
+ Hx8fHx8fHx8fGwAAARQXFxcXFxcXFxcXFxcXFxcXFxcXAQAAAAAAAAAAAAAAGyAhISEhISEhISEgGwAA
+ ARYZGRkZGRkZGRkZGRkZGRkZGRkZAQAAAAAAAAAAAAAAGyEhISEhISEhISEhGwAAARgaGhoaGhoaGhoa
+ GhoaGhoaGhoaAQAAAAAAAAAAAAAAGyIiIyIjIyIjIyMiGwAAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAA
+ AAAAAAAAAAAAGyUlJSUlJSUlJSUlGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGyYm
+ JiYmJiYmJiYmGyQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJEIAGyYnJygnKCcoJycnHCQv
+ Ly8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vJEEAGygpKSkpKSkpKSkpHCQwLzAwMDAwMDAwMDAw
+ MDAwMDAwMDAwMDAwMDAwMDAvJEEAGykrKiorKisqKyoqHCQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw
+ MDAwMDAwJEEAGyosLCwsLCwsLCwsGyQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwJEEAGywt
+ LS0tLS0tLS0tHCQxMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExJEEAGy0uLi4uLi4uLi4uHCQy
+ MjIxMjEyMTIxMjEyMTIxMjEyMTIxMjEyMTIxMjEyJEEAGy4uLi4uLi4uLi4uGyQyMjIyMjIyMjIyMjIy
+ MjIyMjIyMjIyMjIyMjIyMjIyJEEAGyorKysrKysrKysrGyQzMzQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0
+ NDQ0NDQzJEEAGxsbGxsbGxsbGxsbHCQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDU0JEEAAAAA
+ AAAAAAAAAAAAACQ1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1JEEAAAAAAAAAAAAAAAAAACQ1
+ NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1JEEAAAAAAAAAAAAAAAAAACQ2Njc3Nzc3Nzc3Nzc3
+ Nzc3Nzc3Nzc3Nzc3Nzc3NzY2JEEAAAAAAAAAAAAAAAAAACQ3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3
+ Nzc3Nzg3JEEAAAAAAAAAAAAAAAAAACQ4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4JEEAAAAA
+ AAAAAAAAAAAAACQ4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4JEEAAAAAAAAAAAAAAAAAACQ5
+ OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5JEEAAAAAAAAAAAAAAAAAACQ6Ojo6Ojo6Ojo6Ojo6
+ Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6JEEAAAAAAAAAAAAAAAAAACQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7
+ Ozs7Ozs7JEEAAAAAAAAAAAAAAAAAACQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7JEEAAAAA
+ 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
+ 3LH/AOvR/wD///8AAAAAAAgALwAOAFAAFQBwABsAkAAhALAAJgDPACwA8AA+Ef8AWDH/AHFR/wCMcf8A
+ ppH/AL+x/wDa0f8A////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB
+ AQEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAECAgICAgICAgICAgIBKQAAAAAAAAAAAAAAAAAA
+ AAAAAQMDAwMDAwMDAwMDAwEpAAAAAAAAAAAAAAAAAAAAAAABBAQEBAQEBAQEBAQEASkAAAAAAAAAAAAA
+ AAAAAAAAAAEFBQUFBQUFBQUFBQUBKQAAAAAAAAAAAAAAAAAAAAAAAQYGBgYGBgYGBgYGBgEpAAAAAAAA
+ LAsLCwsLCwsLAAABBwcHBwcHBwcHBwcHASkAAAAAAAAsCwwMDAwMDAsYAAEICAgICAgICAgICAgBKQAA
+ AAAAACwLDQ0NDQ0NCxgAAQkJCQkJCQkJCQkJCQEpAAAAAAAALAsODg4ODg4LGAABCgoKCgoKCgoKCgoK
+ ASkAAAAAAAAsCw8PDw8PDwsYAAEBAQEBAQEBAQEBAQEBKQAAAAAAACwLERERERERCxcAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAALAsSEhISEhILEBAQEBAQEBAQEBAQEBAQEBAQEBAQECosCxMTExMTEwsQGhoaGhoa
+ GhoaGhoaGhoaGhoaGhoQGSwLFBQUFBQUCxAbGxsbGxsbGxsbGxsbGxsbGxsbGxAZLAsVFRUVFRULEBwc
+ HBwcHBwcHBwcHBwcHBwcHBwcEBktCxYWFhYWFgsQHR0dHR0dHR0dHR0dHR0dHR0dHR0QGS0LCwsLCwsL
+ CxAeHh4eHh4eHh4eHh4eHh4eHh4eHhAZAAAAAAAAAAAAEB8fHx8fHx8fHx8fHx8fHx8fHx8fEBkAAAAA
+ AAAAAAAQICAgICAgICAgICAgICAgICAgICAQGQAAAAAAAAAAABAhISEhISEhISEhISEhISEhISEhIRAZ
+ AAAAAAAAAAAAECIiIiIiIiIiIiIiIiIiIiIiIiIiEBkAAAAAAAAAAAAQIyMjIyMjIyMjIyMjIyMjIyMj
+ IyMQGQAAAAAAAAAAABAkJCQkJCQkJCQkJCQkJCQkJCQkJBAZAAAAAAAAAAAAECUkJSQlJCQkJCQkJCQk
+ JCQkJCQkEBkAAAAAAAAAAAAQJSUlJSUlJSUlJSUlJSUlJSUlJSUQGQAAAAAAAAAAABAmJiYmJiYmJiYm
+ JiYmJiYmJiYmJhAZAAAAAAAAAAAAECcnJycnJycnJycnJycnJycnJycnEBkAAAAAAAAAAAAQKCgoKCgo
+ KCgoKCgoKCgoKCgoKCgQGQAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQEBArAAAAAAAAAAAALy4u
+ Li4uLi4uLi4uLi4uLi4uLi4uLjD//////+AAf//gAD//4AA//+AAP//gAD//4AA/AGAAPwAgAD8AIAA/
+ ACAAPwAgAD8AP///AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/4AAAP+AAAD/gAAA/4AAAP+AAAD/gAAA
+ /4AAAP+AAAD/gAAA/4AAAP+AAAD/gAAA/4AAACgAAAAQAAAAIAAAAAEACAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAoj8AAK5OAAC6XQAAr2MPAMZtAACqbEwAsHNMALZ7TACwflQAvINMALKDbwC2h28A
+ uYxvAL2RbwC2jnQAADerAAA8tgAQUL8AQGi9AABCwgAAR80AEFPEAABM2AAlW8AAQGrDAD6w3wA+tuEA
+ PrzkAHiGwgB4iMUAcIzKAHiJyQB4jM0AZI3QAHCT2QBftdwAX7neAGCt2ABgstoAR7zmAF+/4QA9wucA
+ PcjqADzO7QA81O8AO9ryAEfD6QBGyuwAXsLhAEbR7wBG2PIARd/1AEXl+ABE7PwAu7zZALu93ACBuuAA
+ g73iAJrA3wClw9sApsbcALfH3AD///8AAAAAAAAvDgAAUBgAAHAiAACQLAAAsDYAAM9AAADwSgAR/1sA
+ Mf9xAFH/hwBx/50Akf+yALH/yQDR/98A////AAAAAAACLwAABFAAAAZwAAAIkAAACrAAAAvPAAAO8AAA
+ IP8SAD3/MQBb/1EAef9xAJj/kQC1/7EA1P/RAP///wAAAAAAFC8AACJQAAAwcAAAPZAAAEywAABZzwAA
+ Z/AAAHj/EQCK/zEAnP9RAK7/cQDA/5EA0v+xAOT/0QD///8AAAAAACYvAABAUAAAWnAAAHSQAACOsAAA
+ qc8AAMLwAADR/xEA2P8xAN7/UQDj/3EA6f+RAO//sQD2/9EA////AAAAAAAvJgAAUEEAAHBbAACQdAAA
+ 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
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAGYzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2Yz
+ M2YzM2YzM2YzM2YzM2YzM2YzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM5o5A546AJ46AJ46AJ46AJ46AJ46
+ AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AGYzMwAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AGYzM54+A6I/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/
+ AKI/AGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM6JCA6ZDAKZDAKZDAKZDAKZDAKZDAKZDAKZDAKZDAKZD
+ AKZDAKZDAKZDAKZDAKZDAKZDAKZDAKZDAKZDAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM6ZHA6pJAKpJ
+ AKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAGYzMwAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAGYzM6lMA65OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5O
+ AK5OAK5OAK5OAK5OAK5OAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM61RA7JTALJTALJTALJTALJTALJT
+ ALJTALJTALJTALJTALJTALJTALJTALJTALJTALJTALJTALJTALJTAGYzMwAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AGYzM7FWA7ZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZ
+ ALZZAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQAzoQAzoQAzoQAzoQAzoQAz
+ oQAzoQAzoQAzoQAzoQAzoQAzoQAAAAAAAGYzM7RaA7pdALpdALpdALpdALpdALpdALpdALpdALpdALpd
+ ALpdALpdALpdALpdALpdALpdALpdALpdALpdAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAzoQA0owA0pAA0pAA0pAA0pAA0pAA0pAA0pAA0pAA0pAA0pAAzoQAAAAAAAGYzM7hfA75iAL5i
+ AL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAGYzMwAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA2pwA2qAA2qAA2qAA2qAA2qAA2qAA2qAA2qAA2
+ qAA2qAAzoQAAAAAAAGYzM7xkA8JoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJo
+ AMJoAMJoAMJoAMJoAMJoAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA3qwA3
+ qwA3qwA3qwA3qwA3qwA3qwA3qwA3qwA3qwA3qwAzoQAAAAAAAGYzM8BpA8ZtAMZtAMZtAMZtAMZtAMZt
+ AMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAGYzMwAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAzoQA5rgA5rwA5rwA5rwA5rwA5rwA5rwA5rwA5rwA5rwA5rwAzoQAAAAAA
+ AGYzM8NuA8pyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpy
+ AMpyAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA6sQA7swA7swA7swA7swA7
+ swA7swA7swA7swA7swA7swAzoQAAAAAAAGYzM8dzA853AM53AM53AM53AM53AM53AM53AM53AM53AM53
+ AM53AM53AM53AM53AM53AM53AM53AM53AM53AGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAzoQA8tQA8tgA8tgA8tgA8tgA8tgA8tgA8tgA8tgA8tgA8tgAzoQAAAAAAAGYzM8t3A9J8ANJ8
+ ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8AGYzMwAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA9uAA+ugA+ugA+ugA+ugA+ugA+ugA+ugA+ugA+
+ ugA+ugAzoQAAAAAAAGYzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2Yz
+ M2YzM2YzM2YzM2YzM2YzM2YzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA/vABA
+ vgBAvgBAvgBAvgBAvgBAvgBAvgBAvgBAvgBAvgAzoQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAzoQBBvwBBwQBBwQBBwQBBwQBBwQBBwQBBwQBBwQBBwQBBwQAzoQBbrwBb
+ rwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBb
+ rwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbr7rH3wAAAAAzoQBCwwBDxQBDxQBDxQBDxQBD
+ xQBDxQBDxQBDxQBDxQBDxQAzoQBbr0i45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei4
+ 5Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45ABbr6y8
+ 2QAAAAAzoQBExgBFyQBFyQBFyQBFyQBFyQBFyQBFyQBFyQBFyQBFyQAzoQBbr0i65Ui65Ui65Ui65Ui6
+ 5Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui6
+ 5Ui65Ui65Ui65Ui65Ui65Ui65QBbr6y82QAAAAAzoQBFygBHzABHzABHzABHzABHzABHzABHzABHzABH
+ zABHzAAzoQBbr0i85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki8
+ 5ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85gBbr6y82QAAAAAzoQBHzQBI
+ 0ABI0ABI0ABI0ABI0ABI0ABI0ABI0ABI0ABI0AAzoQBbr0i/50i/50i/50i/50i/50i/50i/50i/50i/
+ 50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/
+ 50i/50i/5wBbr6y82QAAAAAzoQBI0QBK1ABK1ABK1ABK1ABK1ABK1ABK1ABK1ABK1ABK1AAzoQBbr0fB
+ 6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB
+ 6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6ABbr6y82QAAAAAzoQBK1ABM2ABM2ABM2ABM2ABM
+ 2ABM2ABM2ABM2ABM2ABM2AAzoQBbr0fD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD
+ 6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6QBbr6y8
+ 2QAAAAAzoQBM2ABN2wBN2wBN2wBN2wBN2wBN2wBN2wBN2wBN2wBN2wAzoQBbr0fF6kfF6kfF6kfF6kfF
+ 6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF
+ 6kfF6kfF6kfF6kfF6kfF6kfF6gBbr6y82QAAAAAzoQBGzABIzwBIzwBIzwBIzwBIzwBIzwBIzwBIzwBI
+ zwBIzwAzoQBbr0fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI
+ 60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI6wBbr6y82QAAAAAzoQAzoQAz
+ oQAzoQAzoQAzoQAzoQAzoQAzoQAzoQAzoQAzoQAzoQBbr0fK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK
+ 7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK
+ 7EfK7EfK7ABbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0fM
+ 7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM
+ 7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7QBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAABbr0bO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO
+ 7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7gBbr6y8
+ 2QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0bR70bR70bR70bR70bR
+ 70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR
+ 70bR70bR70bR70bR70bR70bR7wBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAABbr0bT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT
+ 8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8ABbr6y82QAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0bV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV
+ 8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV
+ 8kbV8kbV8gBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0bX
+ 80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX
+ 80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX8wBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAABbr0ba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba
+ 9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9ABbr6y8
+ 2QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xc9UXc9UXc9UXc9UXc
+ 9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc
+ 9UXc9UXc9UXc9UXc9UXc9UXc9QBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAABbr0Xe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe
+ 9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9gBbr6y82QAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg
+ 90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg
+ 90Xg90Xg9wBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xj
+ +EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj
+ +EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+ABbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl
+ +UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+QBbr6y8
+ 2QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xn+kXn+kXn+kXn+kXn
+ +kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn
+ +kXn+kXn+kXn+kXn+kXn+kXn+gBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAABbr0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp
+ +0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+wBbr6y82QAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Ts/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs
+ /ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs
+ /ETs/ETs/ABbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Tu
+ /UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu
+ /UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/QBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAABbr0Tw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw
+ /kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/gBbr669
+ 2QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbrwBbrwBbrwBbrwBbrwBb
+ rwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBb
+ rwBbrwBbrwBbrwBbrwBbrwBbrwBbr+XZ4gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7i4n///////g7u////
+ ////Du7//wAAA/8O7v//AAAD/w7u//8AAAP/Du7//wAAA/8O7v//AAAD/w7u//8AAAP/Du7//wAAA/8O
+ 7v//AAAD/w7ugAMAAAP/Du6AAwAAA/8O7oADAAAD/w7ugAMAAAP/Du6AAwAAA/8O7oADAAAD/w7ugAMA
+ AAP/Du6AAwAAA/8O7oAD/////w7ugAAAAAAADu6AAAAAAAAO7oAAAAAAAA7ugAAAAAAADu6AAAAAAAAO
+ 7oAAAAAAAA7ugAAAAAAADu6AAAAAAAAO7oAAAAAAAA7ugAAAAAAADu7//AAAAAAO7v/8AAAAAA7u//wA
+ AAAADu7//AAAAAAO7v/8AAAAAA7u//wAAAAADu7//AAAAAAO7v/8AAAAAA7u//wAAAAADu7//AAAAAAO
+ 7v/8AAAAAA7u//wAAAAADu7//AAAAAAO7v/8AAAAAA7u//wAAAAADu7//AAAAAAO7v/8AAAAAA7u//wA
+ AAAADu7///////4O7igAAAAgAAAAQAAAAAEAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AABmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzMAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmMzOfOwCfOwCfOwCfOwCfOwCfOwCf
+ OwCfOwCfOwCfOwCfOwCfOwBmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAABmMzOkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQBmMzOxmJgA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmMzOrSgCrSgCr
+ SgCrSgCrSgCrSgCrSgCrSgCrSgCrSgCrSgCrSgBmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmMzOwUQCwUQCwUQCwUQCwUQCwUQCwUQCwUQCwUQCwUQCw
+ UQCwUQBmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AABmMzO3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgBmMzOxmJgAAAAAAAAAAAAAAAAA
+ AAAAAADHu9QAM6EAM6EAM6EAM6EAM6EAM6EAM6EAM6EAAAAAAABmMzO8YAC8YAC8YAC8YAC8YAC8YAC8
+ YAC8YAC8YAC8YAC8YAC8YABmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAADHu9QAM6EANqkANqkANqkANqkA
+ NqkANqkAM6F/mM8AAABmMzPDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQBmMzOxmJgA
+ AAAAAAAAAAAAAAAAAAAAAADHu9QAM6EAOK0AOK0AOK0AOK0AOK0AOK0AM6F/mM8AAABmMzPIcADIcADI
+ cADIcADIcADIcADIcADIcADIcADIcADIcADIcABmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAADHu9QAM6EA
+ O7QAO7QAO7QAO7QAO7QAO7QAM6F/mM8AAABmMzPPeADPeADPeADPeADPeADPeADPeADPeADPeADPeADP
+ eADPeABmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAADHu9QAM6EAPbgAPbgAPbgAPbgAPbgAPbgAM6F/mM8A
+ AABmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzOxmJgAAAAAAAAAAAAAAAAA
+ AAAAAADHu9QAM6EAQL8AQL8AQL8AQL8AQL8AQL8AM6FVfMEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADHu9QAM6EAQsMAQsMAQsMAQsMA
+ QsMAQsMAM6EAW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68A
+ W68AW68AW68AW68AW6+BpM7Hu9QAM6EARcoARcoARcoARcoARcoARcoAM6EAW69IuuVIuuVIuuVIuuVI
+ uuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuUAW698oMzHu9QAM6EA
+ R84AR84AR84AR84AR84AR84AM6EAW69IvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZI
+ vuZIvuZIvuZIvuZIvuZIvuZIvuZIvuYAW698oMzHu9QAM6EAStUAStUAStUAStUAStUAStUAM6EAW69H
+ wehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwegA
+ W698oMzHvNQAM6EATNoATNoATNoATNoATNoATNoAM6EAW69HxOlHxOlHxOlHxOlHxOlHxOlHxOlHxOlH
+ xOlHxOlHxOlHxOlHxOlHxOlHxOlHxOlHxOlHxOlHxOlHxOkAW698oMzHvNQAM6EAM6EAM6EAM6EAM6EA
+ M6EAM6EAM6EAW69HyOtHyOtHyOtHyOtHyOtHyOtHyOtHyOtHyOtHyOtHyOtHyOtHyOtHyOtHyOtHyOtH
+ yOtHyOtHyOtHyOsAW698oMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW69Hy+xHy+xHy+xHy+xH
+ y+xHy+xHy+xHy+xHy+xHy+xHy+xHy+xHy+xHy+xHy+xHy+xHy+xHy+xHy+xHy+wAW698oMwAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAW69Gz+5Gz+5Gz+5Gz+5Gz+5Gz+5Gz+5Gz+5Gz+5Gz+5Gz+5Gz+5G
+ z+5Gz+5Gz+5Gz+5Gz+5Gz+5Gz+5Gz+4AW698oMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW69G
+ 0u9G0u9G0u9G0u9G0u9G0u9G0u9G0u9G0u9G0u9G0u9G0u9G0u9G0u9G0u9G0u9G0u9G0u9G0u9G0u8A
+ W698oMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW69G1fJG1fJG1fJG1fJG1fJG1fJG1fJG1fJG
+ 1fJG1fJG1fJG1fJG1fJG1fJG1fJG1fJG1fJG1fJG1fJG1fIAW698oMwAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAW69G2fNG2fNG2fNG2fNG2fNG2fNG2fNG2fNG2fNG2fNG2fNG2fNG2fNG2fNG2fNG2fNG
+ 2fNG2fNG2fNG2fMAW698oMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW69F3PVF3PVF3PVF3PVF
+ 3PVF3PVF3PVF3PVF3PVF3PVF3PVF3PVF3PVF3PVF3PVF3PVF3PVF3PVF3PVF3PUAW698oMwAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAW69F3/ZF3/ZF3/ZF3/ZF3/ZF3/ZF3/ZF3/ZF3/ZF3/ZF3/ZF3/ZF
+ 3/ZF3/ZF3/ZF3/ZF3/ZF3/ZF3/ZF3/YAW698oMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW69F
+ 4/hF4/hF4/hF4/hF4/hF4/hF4/hF4/hF4/hF4/hF4/hF4/hF4/hF4/hF4/hF4/hF4/hF4/hF4/hF4/gA
+ W698oMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW69F5vlF5vlF5vlF5vlF5vlF5vlF5vlF5vlF
+ 5vlF5vlF5vlF5vlF5vlF5vlF5vlF5vlF5vlF5vlF5vlF5vkAW698oMwAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAW69E6vtE6vtE6vtE6vtE6vtE6vtE6vtE6vtE6vtE6vtE6vtE6vtE6vtE6vtE6vtE6vtE
+ 6vtE6vtE6vtE6vsAW698oMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW69E7fxE7fxE7fxE7fxE
+ 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
+ bQIAK8YArNre91lwpL6t0TYBgBVjAFZp5/ssOFLfTdU2AYAVYwBWJgCxUd8TAJTAGIBGbX2fBUfqh2na
+ JgCwYgzASm18nwVHaul0bRMAWDEFAPEjALBCAMoTAYAVAlCeCACsEIDyRAAARI8AAAkjAEDCCACQMAIA
+ JIwAAAkjAEDCCACQMFMAAKRlbv1PNdVF4Jm637at+5rUfY3qvrL4iScADfsZgOX03tj+IOMAAAAASUVO
+ RK5CYIIoAAAAMAAAAGAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/svLi/7Ly2D+y8tb/svL
+ W/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svL
+ W/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svL
+ W/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8uU/svL
+ ev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AGUyMhxlMjIgZTIyIGUyMiBlMjIgZTIyIGUyMiBlMjIgZTIyIGUyMiBlMjIgZTIyIGUyMiBlMjIgZTIy
+ IGUyMiBlMjIgZTIyIGUyMiBlMjIgZTIyIGUyMiBlMjIIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAGYzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz
+ /2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+aOQP/njoA/546AP+eOgD/njoA/546AP+eOgD/njoA
+ /546AP+eOgD/njoA/546AP+eOgD/njoA/546AP+eOgD/njoA/546AP+eOgD/njoA/2YzM/9lMjJAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+ePgP/oj8A/6I/AP+iPwD/oj8A
+ /6I/AP+iPwD/oj8A/6I/AP+iPwD/oj8A/6I/AP+iPwD/oj8A/6I/AP+iPwD/oj8A/6I/AP+iPwD/oj8A
+ /2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+iQgP/pkMA
+ /6ZDAP+mQwD/pkMA/6ZDAP+mQwD/pkMA/6ZDAP+mQwD/pkMA/6ZDAP+mQwD/pkMA/6ZDAP+mQwD/pkMA
+ /6ZDAP+mQwD/pkMA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svL
+ ev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AGYzM/+mRwP/qkkA/6pJAP+qSQD/qkkA/6pJAP+qSQD/qkkA/6pJAP+qSQD/qkkA/6pJAP+qSQD/qkkA
+ /6pJAP+qSQD/qkkA/6pJAP+qSQD/qkkA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAGYzM/+pTAP/rk4A/65OAP+uTgD/rk4A/65OAP+uTgD/rk4A/65OAP+uTgD/rk4A
+ /65OAP+uTgD/rk4A/65OAP+uTgD/rk4A/65OAP+uTgD/rk4A/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+tUQP/slMA/7JTAP+yUwD/slMA/7JTAP+yUwD/slMA
+ /7JTAP+yUwD/slMA/7JTAP+yUwD/slMA/7JTAP+yUwD/slMA/7JTAP+yUwD/slMA/2YzM/9lMjJAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+xVgP/tlkA/7ZZAP+2WQD/tlkA
+ /7ZZAP+2WQD/tlkA/7ZZAP+2WQD/tlkA/7ZZAP+2WQD/tlkA/7ZZAP+2WQD/tlkA/7ZZAP+2WQD/tlkA
+ /2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AM6H/ADOh
+ /wAzof8AM6H/ADOh/wAzof8AM6H/ADOh/wAzof8AM6H/ADOh/wAzof8AMqAsAAAAAGYzM/+0WgP/ul0A
+ /7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A
+ /7pdAP+6XQD/ul0A/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svL
+ egAzof8ANKP/ADSk/wA0pP8ANKT/ADSk/wA0pP8ANKT/ADSk/wA0pP8ANKT/ADSk/wAzof8AMqBAAAAA
+ AGYzM/+4XwP/vmIA/75iAP++YgD/vmIA/75iAP++YgD/vmIA/75iAP++YgD/vmIA/75iAP++YgD/vmIA
+ /75iAP++YgD/vmIA/75iAP++YgD/vmIA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAD+y8tb/svLegAzof8ANqf/ADao/wA2qP8ANqj/ADao/wA2qP8ANqj/ADao/wA2qP8ANqj/ADao
+ /wAzof8AMqBAAAAAAGYzM/+8ZAP/wmgA/8JoAP/CaAD/wmgA/8JoAP/CaAD/wmgA/8JoAP/CaAD/wmgA
+ /8JoAP/CaAD/wmgA/8JoAP/CaAD/wmgA/8JoAP/CaAD/wmgA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AN6v/ADer/wA3q/8AN6v/ADer/wA3q/8AN6v/ADer
+ /wA3q/8AN6v/ADer/wAzof8AMqBAAAAAAGYzM//AaQP/xm0A/8ZtAP/GbQD/xm0A/8ZtAP/GbQD/xm0A
+ /8ZtAP/GbQD/xm0A/8ZtAP/GbQD/xm0A/8ZtAP/GbQD/xm0A/8ZtAP/GbQD/xm0A/2YzM/9lMjJAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AOa7/ADmv/wA5r/8AOa//ADmv
+ /wA5r/8AOa//ADmv/wA5r/8AOa//ADmv/wAzof8AMqBAAAAAAGYzM//DbgP/ynIA/8pyAP/KcgD/ynIA
+ /8pyAP/KcgD/ynIA/8pyAP/KcgD/ynIA/8pyAP/KcgD/ynIA/8pyAP/KcgD/ynIA/8pyAP/KcgD/ynIA
+ /2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AOrH/ADuz
+ /wA7s/8AO7P/ADuz/wA7s/8AO7P/ADuz/wA7s/8AO7P/ADuz/wAzof8AMqBAAAAAAGYzM//HcwP/zncA
+ /853AP/OdwD/zncA/853AP/OdwD/zncA/853AP/OdwD/zncA/853AP/OdwD/zncA/853AP/OdwD/zncA
+ /853AP/OdwD/zncA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svL
+ egAzof8APLX/ADy2/wA8tv8APLb/ADy2/wA8tv8APLb/ADy2/wA8tv8APLb/ADy2/wAzof8AMqBAAAAA
+ AGYzM//LdwP/0nwA/9J8AP/SfAD/0nwA/9J8AP/SfAD/0nwA/9J8AP/SfAD/0nwA/9J8AP/SfAD/0nwA
+ /9J8AP/SfAD/0nwA/9J8AP/SfAD/0nwA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAD+y8tb/svLegAzof8APbj/AD66/wA+uv8APrr/AD66/wA+uv8APrr/AD66/wA+uv8APrr/AD66
+ /wAzof8AMqBAAAAAAGYzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz
+ /2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AP7z/AEC+/wBAvv8AQL7/AEC+/wBAvv8AQL7/AEC+
+ /wBAvv8AQL7/AEC+/wAzof8AMqBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AQb//AEHB/wBBwf8AQcH/AEHB
+ /wBBwf8AQcH/AEHB/wBBwf8AQcH/AEHB/wAzof8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv
+ /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv
+ /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/+InsiS/svLegAzof8AQsP/AEPF
+ /wBDxf8AQ8X/AEPF/wBDxf8AQ8X/AEPF/wBDxf8AQ8X/AEPF/wAzof8AW6//SLjk/0i45P9IuOT/SLjk
+ /0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk
+ /0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/wBbr/9+mMWk/svL
+ egAzof8ARMb/AEXJ/wBFyf8ARcn/AEXJ/wBFyf8ARcn/AEXJ/wBFyf8ARcn/AEXJ/wAzof8AW6//SLrl
+ /0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl
+ /0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl
+ /wBbr/9+mMWk/svLegAzof8ARcr/AEfM/wBHzP8AR8z/AEfM/wBHzP8AR8z/AEfM/wBHzP8AR8z/AEfM
+ /wAzof8AW6//SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm
+ /0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm
+ /0i85v9IvOb/SLzm/wBbr/9+mMWk/svLegAzof8AR83/AEjQ/wBI0P8ASND/AEjQ/wBI0P8ASND/AEjQ
+ /wBI0P8ASND/AEjQ/wAzof8AW6//SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n
+ /0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n
+ /0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/wBbr/9+mMWk/svLegAzof8ASNH/AErU/wBK1P8AStT/AErU
+ /wBK1P8AStT/AErU/wBK1P8AStT/AErU/wAzof8AW6//R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho
+ /0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho
+ /0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/wBbr/9+mMWk/8zMegAzof8AStT/AEzY
+ /wBM2P8ATNj/AEzY/wBM2P8ATNj/AEzY/wBM2P8ATNj/AEzY/wAzof8AW6//R8Pp/0fD6f9Hw+n/R8Pp
+ /0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp
+ /0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/wBbr/9+mMWk/8zM
+ egAzof8ATNj/AE3b/wBN2/8ATdv/AE3b/wBN2/8ATdv/AE3b/wBN2/8ATdv/AE3b/wAzof8AW6//R8Xq
+ /0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq
+ /0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq
+ /wBbr/9+mMWk/8zMegAzof8ARsz/AEjP/wBIz/8ASM//AEjP/wBIz/8ASM//AEjP/wBIz/8ASM//AEjP
+ /wAzof8AW6//R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr
+ /0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr
+ /0fI6/9HyOv/R8jr/wBbr/9+mMWk/8zMegAzof8AM6H/ADOh/wAzof8AM6H/ADOh/wAzof8AM6H/ADOh
+ /wAzof8AM6H/ADOh/wAzof8AW6//R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs
+ /0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs
+ /0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt
+ /0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt
+ /0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rs7u/0bO7v9Gzu7/Rs7u
+ /0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u
+ /0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/wBbr/9+mMWk/8zM
+ ev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//RtHv
+ /0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv
+ /0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv
+ /wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAW6//RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw
+ /0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw
+ /0bT8P9G0/D/RtPw/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAW6//RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy
+ /0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy
+ /0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz
+ /0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz
+ /0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rtr0/0ba9P9G2vT/Rtr0
+ /0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0
+ /0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/wBbr/9+mMWk/svL
+ ev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rdz1
+ /0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1
+ /0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1
+ /wBbr/9+mMWk/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAW6//Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72
+ /0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72
+ /0Xe9v9F3vb/Rd72/wBbr/9+mMWk/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAW6//ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3
+ /0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3
+ /0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/wBbr/9+mMWk/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4
+ /0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4
+ /0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/wBbr/9+mMWk/svLev7LywgAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//ReX5/0Xl+f9F5fn/ReX5
+ /0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5
+ /0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/wBbr/9+mMWk/svL
+ ev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Ref6
+ /0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6
+ /0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6
+ /wBbr/9+mMWk/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAW6//ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7
+ /0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7
+ /0Tp+/9E6fv/ROn7/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAW6//ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8
+ /0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8
+ /0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/wBbr/9+mMWk/svLev/LywgAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79
+ /0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79
+ /0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/wBbr/9+mMWk/svLev7LywgAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//RPD+/0Tw/v9E8P7/RPD+
+ /0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+
+ /0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/wBbr/+BmMSi/svL
+ ev7Lyw7+y8sI/8zMCP/MzAj+y8sI/svLCP7Lywj+y8sI/svLCP7Lywj+y8sI/8zMCP/MzAgAW6//AFuv
+ /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv
+ /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv
+ /wBbr//NtcaA/svLfP/Ly3r+y8t6/8zMev/MzHr+y8t6/svLev7Ly3r+y8t6/svLev7Ly3r+y8t6/8zM
+ ev/MzHr/zMx6/8zMev/MzHr/zMx6/8zMev/MzHr/zMx6/8zMev/MzHr+y8t6/svLev7Ly3r+y8t6/svL
+ ev7Ly3r+y8t6/svLev7Ly3r+y8t6/svLev7Ly3r+y8t6/svLev7Ly3r+y8t6/svLev7Ly3r+y8t6/svL
+ ev7Ly3r+y8t6/svLev7Ly3r+y8uLAAAAAAAADu4//wAAAf4O7j//AAAB/g7uP/8AAAH+Du4//wAAAf4O
+ 7j//AAAB/g7uP/8AAAH+Du4//wAAAf4O7j//AAAB/g7uP/8AAAH+Du4AAQAAAf4O7gABAAAB/g7uAAEA
+ AAH+Du4AAQAAAf4O7gABAAAB/g7uAAEAAAH+Du4AAQAAAf4O7gABAAAB/g7uAAH////+Du4AAAAAAAAO
+ 7gAAAAAAAA7uAAAAAAAADu4AAAAAAAAO7gAAAAAAAA7uAAAAAAAADu4AAAAAAAAO7gAAAAAAAA7uAAAA
+ AAAADu4AAAAAAAAO7j/8AAAAAA7uP/wAAAAADu4//AAAAAAO7j/8AAAAAA7uP/wAAAAADu4//AAAAAAO
+ 7j/8AAAAAA7uP/wAAAAADu4//AAAAAAO7j/8AAAAAA7uP/wAAAAADu4//AAAAAAO7j/8AAAAAA7uP/wA
+ AAAADu4//AAAAAAO7j/8AAAAAA7uP/wAAAAADu4AAAAAAAAO7gAAAAAAAA7uKAAAACAAAABAAAAAAQAg
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7Ly2/+y8s//svLPf7Lyz3+y8s9/svLPf7Lyz3+y8s9/svL
+ Pf7Lyz3hrq5Ay5iYR8uYmEfLmJhHy5iYR8uYmEfLmJhHy5iYR8uYmEfLmJhHy5iYR8uYmEfLmJhHy5iY
+ R8uYmEfLmJhC/svLPf7Lyz3+y8s9/svLPf7Lyz3+y8tq/svLVP7LywMAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAGUyMjxmMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz
+ /2YzM/9mMzP/ZjMz/2UyMloAAAAAAAAAAAAAAAAAAAAAAAAAAP7Lyz3+y8tU/svLAwAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAZjMzVWYzM/+fOwD/nzsA/587AP+fOwD/nzsA/587AP+fOwD/nzsA
+ /587AP+fOwD/nzsA/587AP9mMzP/ZTIygAAAAAAAAAAAAAAAAAAAAAAAAAAA/svLPf7Ly1T+y8sDAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmMzNVZjMz/6RBAP+kQQD/pEEA/6RBAP+kQQD/pEEA
+ /6RBAP+kQQD/pEEA/6RBAP+kQQD/pEEA/2YzM/9lMjKAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8s9/svL
+ VP7LywMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM1VmMzP/q0oA/6tKAP+rSgD/q0oA
+ /6tKAP+rSgD/q0oA/6tKAP+rSgD/q0oA/6tKAP+rSgD/ZjMz/2UyMoAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AP7Lyz3+y8tU/svLAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZjMzVWYzM/+wUQD/sFEA
+ /7BRAP+wUQD/sFEA/7BRAP+wUQD/sFEA/7BRAP+wUQD/sFEA/7BRAP9mMzP/ZTIygAAAAAAAAAAAAAAA
+ AAAAAAAAAAAA/svLPeG6xm9da7BXADOhVQAzoVUAM6FVADOhVQAzoVUAM6FVADOhVQAyoCZmMzNVZjMz
+ /7daAP+3WgD/t1oA/7daAP+3WgD/t1oA/7daAP+3WgD/t1oA/7daAP+3WgD/t1oA/2YzM/9lMjKAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAD+y8s9qZi9pgAzof8AM6H/ADOh/wAzof8AM6H/ADOh/wAzof8AM6H/ADKg
+ e2YzM1VmMzP/vGAA/7xgAP+8YAD/vGAA/7xgAP+8YAD/vGAA/7xgAP+8YAD/vGAA/7xgAP+8YAD/ZjMz
+ /2UyMoAAAAAAAAAAAAAAAAAAAAAAAAAAAP7Lyz2pmL2mADOh/wA2qf8ANqn/ADap/wA2qf8ANqn/ADap
+ /wAzof8AMqCAZjMzVWYzM//DaQD/w2kA/8NpAP/DaQD/w2kA/8NpAP/DaQD/w2kA/8NpAP/DaQD/w2kA
+ /8NpAP9mMzP/ZTIygAAAAAAAAAAAAAAAAAAAAAAAAAAA/svLPamYvaYAM6H/ADit/wA4rf8AOK3/ADit
+ /wA4rf8AOK3/ADOh/wAyoIBmMzNVZjMz/8hwAP/IcAD/yHAA/8hwAP/IcAD/yHAA/8hwAP/IcAD/yHAA
+ /8hwAP/IcAD/yHAA/2YzM/9lMjKAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8s9qZi9pgAzof8AO7T/ADu0
+ /wA7tP8AO7T/ADu0/wA7tP8AM6H/ADKggGYzM1VmMzP/z3gA/894AP/PeAD/z3gA/894AP/PeAD/z3gA
+ /894AP/PeAD/z3gA/894AP/PeAD/ZjMz/2UyMoAAAAAAAAAAAAAAAAAAAAAAAAAAAP7Lyz2pmL2mADOh
+ /wA9uP8APbj/AD24/wA9uP8APbj/AD24/wAzof8AMqCAZjMzVWYzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz
+ /2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZTIygAAAAAAAAAAAAAAAAAAAAAAAAAAA/svL
+ PamYvaYAM6H/AEC//wBAv/8AQL//AEC//wBAv/8AQL//ADOh/wA7o6oAW69VAFuvVQBbr1UAW69VAFuv
+ VQBbr1UAW69VAFuvVQBbr1UAW69VAFuvVQBbr1UAW69VAFuvVQBbr1UAW69VAFuvVQBbr1UAW69VAFuv
+ VQBbr1WsqMRlqZi9pgAzof8AQsP/AELD/wBCw/8AQsP/AELD/wBCw/8AM6H/AFuv/wBbr/8AW6//AFuv
+ /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv
+ /wBbr/8AW6//AFuv/1aFvr6pmL2mADOh/wBFyv8ARcr/AEXK/wBFyv8ARcr/AEXK/wAzof8AW6//SLrl
+ /0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl
+ /0i65f9IuuX/SLrl/0i65f8AW6//VIO9wqmYvaYAM6H/AEfO/wBHzv8AR87/AEfO/wBHzv8AR87/ADOh
+ /wBbr/9Ivub/SL7m/0i+5v9Ivub/SL7m/0i+5v9Ivub/SL7m/0i+5v9Ivub/SL7m/0i+5v9Ivub/SL7m
+ /0i+5v9Ivub/SL7m/0i+5v9Ivub/SL7m/wBbr/9Ug73CqZi9pgAzof8AStX/AErV/wBK1f8AStX/AErV
+ /wBK1f8AM6H/AFuv/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho
+ /0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/AFuv/1SDvcKqmb2mADOh/wBM2v8ATNr/AEza
+ /wBM2v8ATNr/AEza/wAzof8AW6//R8Tp/0fE6f9HxOn/R8Tp/0fE6f9HxOn/R8Tp/0fE6f9HxOn/R8Tp
+ /0fE6f9HxOn/R8Tp/0fE6f9HxOn/R8Tp/0fE6f9HxOn/R8Tp/0fE6f8AW6//VIO9wqqZvaYAM6H/ADOh
+ /wAzof8AM6H/ADOh/wAzof8AM6H/ADOh/wBbr/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr
+ /0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/wBbr/9Ug73C4rvH
+ b15rsFcAM6FVADOhVQAzoVUAM6FVADOhVQAzoVUAM6FVAFuv/0fL7P9Hy+z/R8vs/0fL7P9Hy+z/R8vs
+ /0fL7P9Hy+z/R8vs/0fL7P9Hy+z/R8vs/0fL7P9Hy+z/R8vs/0fL7P9Hy+z/R8vs/0fL7P9Hy+z/AFuv
+ /1SDvcL/zMxU/8zMAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rs/u/0bP7v9Gz+7/Rs/u
+ /0bP7v9Gz+7/Rs/u/0bP7v9Gz+7/Rs/u/0bP7v9Gz+7/Rs/u/0bP7v9Gz+7/Rs/u/0bP7v9Gz+7/Rs/u
+ /0bP7v8AW6//VIO9wv/MzFT/zMwDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr/9G0u//RtLv
+ /0bS7/9G0u//RtLv/0bS7/9G0u//RtLv/0bS7/9G0u//RtLv/0bS7/9G0u//RtLv/0bS7/9G0u//RtLv
+ /0bS7/9G0u//RtLv/wBbr/9Ug73C/8zMVP/MzAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFuv
+ /0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy
+ /0bV8v9G1fL/RtXy/0bV8v9G1fL/AFuv/1SDvcL/zMxU/8zMAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAW6//Rtnz/0bZ8/9G2fP/Rtnz/0bZ8/9G2fP/Rtnz/0bZ8/9G2fP/Rtnz/0bZ8/9G2fP/Rtnz
+ /0bZ8/9G2fP/Rtnz/0bZ8/9G2fP/Rtnz/0bZ8/8AW6//VIO9wv7Ly1T+y8sDAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAABbr/9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1
+ /0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/wBbr/9Ug73C/svLVP7LywMAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAFuv/0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/Rd/2
+ /0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/AFuv/1SDvcL+y8tU/svL
+ AwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4
+ /0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P8AW6//VIO9
+ wv7Ly1T+y8sDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr/9F5vn/Reb5/0Xm+f9F5vn/Reb5
+ /0Xm+f9F5vn/Reb5/0Xm+f9F5vn/Reb5/0Xm+f9F5vn/Reb5/0Xm+f9F5vn/Reb5/0Xm+f9F5vn/Reb5
+ /wBbr/9Ug73C/svLVP7LywMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFuv/0Tq+/9E6vv/ROr7
+ /0Tq+/9E6vv/ROr7/0Tq+/9E6vv/ROr7/0Tq+/9E6vv/ROr7/0Tq+/9E6vv/ROr7/0Tq+/9E6vv/ROr7
+ /0Tq+/9E6vv/AFuv/1SDvcL+y8tU/8vLAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//RO38
+ /0Tt/P9E7fz/RO38/0Tt/P9E7fz/RO38/0Tt/P9E7fz/RO38/0Tt/P9E7fz/RO38/0Tt/P9E7fz/RO38
+ /0Tt/P9E7fz/RO38/0Tt/P8AW6//VIO9wv7Ly1X+y8sF/8zMA/7LywP+y8sD/svLA/7LywP+y8sD/8zM
+ AwBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv
+ /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/9mir25/svLb/7Ly1X/zMxU/svLVP7Ly1T+y8tU/svL
+ VP7Ly1T/zMxUxrLFi6qmwqaqpsKmqqbCpqqmwqaqpsKmqaXBpqmlwaappcGmqaXBpqmlwaappcGmqaXB
+ pqmlwaappcGmqaXBpqmlwaappcGmqaXBpqmlwaappcGmqaXBpta5xpIAAAAAP8AAPj/AAD4/wAA+P8AA
+ Pj/AAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAAAAAAAAAAAACgAAAAQAAAAIAAA
+ AAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tB/svLHv7Lyx7+y8se/svLHp1qanCYZWWjmGVl
+ o5hlZaOYZWWjmGVlo5hlZaOYZWV4/svLHv7Lyx7+y8s5/svLLAAAAAAAAAAAAAAAAAAAAABmMzP/ZjMz
+ /2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/wAAAAAAAAAA/svLHv7LyywAAAAAAAAAAAAAAAAAAAAAZjMz
+ /61NAP+tTQD/rU0A/61NAP+tTQD/rU0A/2YzM/8AAAAAAAAAAP7Lyx4AM6H/ADOh/wAzof8AM6H/ADOh
+ /2YzM/+5XQD/uV0A/7ldAP+5XQD/uV0A/7ldAP9mMzP/AAAAAAAAAAD+y8seADOh/wA3q/8AN6v/ADer
+ /wAzof9mMzP/xWwA/8VsAP/FbAD/xWwA/8VsAP/FbAD/ZjMz/wAAAAAAAAAA/svLHgAzof8APLb/ADy2
+ /wA8tv8AM6H/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/8AAAAAAAAAAP7Lyx4AM6H/AEHB
+ /wBBwf8AQcH/ADOh/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//ADOh
+ /wBGzP8ARsz/AEbM/wAzof9IvOX/SLzl/0i85f9IvOX/SLzl/0i85f9IvOX/SLzl/0i85f9IvOX/AFuv
+ /wAzof8AS9f/AEvX/wBL1/8AM6H/R8Lo/0fC6P9Hwuj/R8Lo/0fC6P9Hwuj/R8Lo/0fC6P9Hwuj/R8Lo
+ /wBbr/8AM6H/ADOh/wAzof8AM6H/ADOh/0fJ6/9Hyev/R8nr/0fJ6/9Hyev/R8nr/0fJ6/9Hyev/R8nr
+ /0fJ6/8AW6///8zMLAAAAAAAAAAAAAAAAABbr/9G0O7/RtDu/0bQ7v9G0O7/RtDu/0bQ7v9G0O7/RtDu
+ /0bQ7v9G0O7/AFuv///MzCwAAAAAAAAAAAAAAAAAW6//Rtfy/0bX8v9G1/L/Rtfy/0bX8v9G1/L/Rtfy
+ /0bX8v9G1/L/Rtfy/wBbr//+y8ssAAAAAAAAAAAAAAAAAFuv/0Xd9f9F3fX/Rd31/0Xd9f9F3fX/Rd31
+ /0Xd9f9F3fX/Rd31/0Xd9f8AW6///svLLAAAAAAAAAAAAAAAAABbr/9F5Pj/ReT4/0Xk+P9F5Pj/ReT4
+ /0Xk+P9F5Pj/ReT4/0Xk+P9F5Pj/AFuv//7LyywAAAAAAAAAAAAAAAAAW6//ROv7/0Tr+/9E6/v/ROv7
+ /0Tr+/9E6/v/ROv7/0Tr+/9E6/v/ROv7/wBbr//+y8tI/svLLP7Lyyz+y8ssAFuv/wBbr/8AW6//AFuv
+ /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AACsQXgGrEF4BqxBAAasQQAGrEEABqxBAACs
+ QQAArEEAAKxBAACsQXAArEFwAKxBcACsQXAArEFwAKxBAACsQQ==
+
+
+
\ No newline at end of file
diff --git a/branches/ph-plugins/ExtraTools/NtProfiler/Program.cs b/branches/ph-plugins/ExtraTools/NtProfiler/Program.cs
new file mode 100644
index 000000000..372899312
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/NtProfiler/Program.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Windows.Forms;
+
+namespace NtProfiler
+{
+ static class Program
+ {
+ ///
+ /// The main entry point for the application.
+ ///
+ [STAThread]
+ static void Main()
+ {
+ Application.EnableVisualStyles();
+ Application.SetCompatibleTextRenderingDefault(false);
+ Application.Run(new ProfilerWindow());
+ }
+ }
+}
diff --git a/branches/ph-plugins/ExtraTools/NtProfiler/Properties/AssemblyInfo.cs b/branches/ph-plugins/ExtraTools/NtProfiler/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..54c29984e
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/NtProfiler/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("NtProfiler")]
+[assembly: AssemblyDescription("NtProfiler")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("wj32")]
+[assembly: AssemblyProduct("NtProfiler")]
+[assembly: AssemblyCopyright("Copyright © 2009 wj32. Licensed under the GNU GPL, v3.")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("ad8532b1-3718-4e5d-904a-54085eb45e77")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/branches/ph-plugins/ExtraTools/NtProfiler/Properties/Resources.Designer.cs b/branches/ph-plugins/ExtraTools/NtProfiler/Properties/Resources.Designer.cs
new file mode 100644
index 000000000..72cdade61
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/NtProfiler/Properties/Resources.Designer.cs
@@ -0,0 +1,71 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:2.0.50727.4016
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace NtProfiler.Properties
+{
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources
+ {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources()
+ {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager
+ {
+ get
+ {
+ if ((resourceMan == null))
+ {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("NtProfiler.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture
+ {
+ get
+ {
+ return resourceCulture;
+ }
+ set
+ {
+ resourceCulture = value;
+ }
+ }
+ }
+}
diff --git a/branches/ph-plugins/ExtraTools/NtProfiler/Properties/Resources.resx b/branches/ph-plugins/ExtraTools/NtProfiler/Properties/Resources.resx
new file mode 100644
index 000000000..ffecec851
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/NtProfiler/Properties/Resources.resx
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/branches/ph-plugins/ExtraTools/NtProfiler/Properties/Settings.Designer.cs b/branches/ph-plugins/ExtraTools/NtProfiler/Properties/Settings.Designer.cs
new file mode 100644
index 000000000..6fa45666e
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/NtProfiler/Properties/Settings.Designer.cs
@@ -0,0 +1,30 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:2.0.50727.4016
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace NtProfiler.Properties
+{
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "9.0.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
+ {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default
+ {
+ get
+ {
+ return defaultInstance;
+ }
+ }
+ }
+}
diff --git a/branches/ph-plugins/ExtraTools/NtProfiler/Properties/Settings.settings b/branches/ph-plugins/ExtraTools/NtProfiler/Properties/Settings.settings
new file mode 100644
index 000000000..abf36c5d3
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/NtProfiler/Properties/Settings.settings
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/branches/ph-plugins/ExtraTools/NtProfiler/app.config b/branches/ph-plugins/ExtraTools/NtProfiler/app.config
new file mode 100644
index 000000000..b7db28170
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/NtProfiler/app.config
@@ -0,0 +1,3 @@
+
+
+
diff --git a/branches/ph-plugins/ExtraTools/NtProfiler/app.manifest b/branches/ph-plugins/ExtraTools/NtProfiler/app.manifest
new file mode 100644
index 000000000..25c0371c1
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/NtProfiler/app.manifest
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/branches/ph-plugins/ExtraTools/ProcessAnalyzer/MainWindow.Designer.cs b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/MainWindow.Designer.cs
new file mode 100644
index 000000000..0207b7b86
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/MainWindow.Designer.cs
@@ -0,0 +1,353 @@
+namespace ProcessAnalyzer
+{
+ partial class MainWindow
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainWindow));
+ this.menuStrip = new System.Windows.Forms.MenuStrip();
+ this.analyzerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.openProcessToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator();
+ this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.tabControl = new System.Windows.Forms.TabControl();
+ this.tabHandleTracing = new System.Windows.Forms.TabPage();
+ this.listHandleStack = new System.Windows.Forms.ListView();
+ this.columnAddress = new System.Windows.Forms.ColumnHeader();
+ this.columnSymbol = new System.Windows.Forms.ColumnHeader();
+ this.listHandleTraces = new System.Windows.Forms.ListView();
+ this.columnIndex = new System.Windows.Forms.ColumnHeader();
+ this.columnHandle = new System.Windows.Forms.ColumnHeader();
+ this.columnType = new System.Windows.Forms.ColumnHeader();
+ this.columnTid = new System.Windows.Forms.ColumnHeader();
+ this.columnHandleName = new System.Windows.Forms.ColumnHeader();
+ this.buttonSnapshot = new System.Windows.Forms.Button();
+ this.buttonDisableHandleTracing = new System.Windows.Forms.Button();
+ this.buttonEnableHandleTracing = new System.Windows.Forms.Button();
+ this.tabHiddenObjects = new System.Windows.Forms.TabPage();
+ this.labelObjectsScanProgress = new System.Windows.Forms.Label();
+ this.buttonScanHiddenObjects = new System.Windows.Forms.Button();
+ this.listHiddenObjects = new System.Windows.Forms.ListView();
+ this.columnObjectType = new System.Windows.Forms.ColumnHeader();
+ this.columnObjectId = new System.Windows.Forms.ColumnHeader();
+ this.columnObjectInfo = new System.Windows.Forms.ColumnHeader();
+ this.menuStrip.SuspendLayout();
+ this.tabControl.SuspendLayout();
+ this.tabHandleTracing.SuspendLayout();
+ this.tabHiddenObjects.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // menuStrip
+ //
+ this.menuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.analyzerToolStripMenuItem});
+ this.menuStrip.Location = new System.Drawing.Point(0, 0);
+ this.menuStrip.Name = "menuStrip";
+ this.menuStrip.Size = new System.Drawing.Size(695, 24);
+ this.menuStrip.TabIndex = 0;
+ this.menuStrip.Text = "menuStrip1";
+ //
+ // analyzerToolStripMenuItem
+ //
+ this.analyzerToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.openProcessToolStripMenuItem,
+ this.toolStripMenuItem1,
+ this.exitToolStripMenuItem});
+ this.analyzerToolStripMenuItem.Name = "analyzerToolStripMenuItem";
+ this.analyzerToolStripMenuItem.Size = new System.Drawing.Size(64, 20);
+ this.analyzerToolStripMenuItem.Text = "Analyzer";
+ //
+ // openProcessToolStripMenuItem
+ //
+ this.openProcessToolStripMenuItem.Name = "openProcessToolStripMenuItem";
+ this.openProcessToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.O)));
+ this.openProcessToolStripMenuItem.Size = new System.Drawing.Size(198, 22);
+ this.openProcessToolStripMenuItem.Text = "&Open Process...";
+ this.openProcessToolStripMenuItem.Click += new System.EventHandler(this.openProcessToolStripMenuItem_Click);
+ //
+ // toolStripMenuItem1
+ //
+ this.toolStripMenuItem1.Name = "toolStripMenuItem1";
+ this.toolStripMenuItem1.Size = new System.Drawing.Size(195, 6);
+ //
+ // exitToolStripMenuItem
+ //
+ this.exitToolStripMenuItem.Name = "exitToolStripMenuItem";
+ this.exitToolStripMenuItem.Size = new System.Drawing.Size(198, 22);
+ this.exitToolStripMenuItem.Text = "E&xit";
+ this.exitToolStripMenuItem.Click += new System.EventHandler(this.exitToolStripMenuItem_Click);
+ //
+ // tabControl
+ //
+ this.tabControl.Controls.Add(this.tabHandleTracing);
+ this.tabControl.Controls.Add(this.tabHiddenObjects);
+ this.tabControl.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.tabControl.Location = new System.Drawing.Point(0, 24);
+ this.tabControl.Name = "tabControl";
+ this.tabControl.SelectedIndex = 0;
+ this.tabControl.Size = new System.Drawing.Size(695, 469);
+ this.tabControl.TabIndex = 1;
+ //
+ // tabHandleTracing
+ //
+ this.tabHandleTracing.Controls.Add(this.listHandleStack);
+ this.tabHandleTracing.Controls.Add(this.listHandleTraces);
+ this.tabHandleTracing.Controls.Add(this.buttonSnapshot);
+ this.tabHandleTracing.Controls.Add(this.buttonDisableHandleTracing);
+ this.tabHandleTracing.Controls.Add(this.buttonEnableHandleTracing);
+ this.tabHandleTracing.Location = new System.Drawing.Point(4, 22);
+ this.tabHandleTracing.Name = "tabHandleTracing";
+ this.tabHandleTracing.Padding = new System.Windows.Forms.Padding(3);
+ this.tabHandleTracing.Size = new System.Drawing.Size(687, 443);
+ this.tabHandleTracing.TabIndex = 0;
+ this.tabHandleTracing.Text = "Handle Tracing";
+ this.tabHandleTracing.UseVisualStyleBackColor = true;
+ //
+ // listHandleStack
+ //
+ this.listHandleStack.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.listHandleStack.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
+ this.columnAddress,
+ this.columnSymbol});
+ this.listHandleStack.FullRowSelect = true;
+ this.listHandleStack.HideSelection = false;
+ this.listHandleStack.Location = new System.Drawing.Point(6, 226);
+ this.listHandleStack.MultiSelect = false;
+ this.listHandleStack.Name = "listHandleStack";
+ this.listHandleStack.Size = new System.Drawing.Size(675, 211);
+ this.listHandleStack.TabIndex = 1;
+ this.listHandleStack.UseCompatibleStateImageBehavior = false;
+ this.listHandleStack.View = System.Windows.Forms.View.Details;
+ //
+ // columnAddress
+ //
+ this.columnAddress.Text = "Address";
+ this.columnAddress.Width = 100;
+ //
+ // columnSymbol
+ //
+ this.columnSymbol.Text = "Symbol";
+ this.columnSymbol.Width = 300;
+ //
+ // listHandleTraces
+ //
+ this.listHandleTraces.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.listHandleTraces.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
+ this.columnIndex,
+ this.columnHandle,
+ this.columnType,
+ this.columnTid,
+ this.columnHandleName});
+ this.listHandleTraces.FullRowSelect = true;
+ this.listHandleTraces.HideSelection = false;
+ this.listHandleTraces.Location = new System.Drawing.Point(6, 35);
+ this.listHandleTraces.MultiSelect = false;
+ this.listHandleTraces.Name = "listHandleTraces";
+ this.listHandleTraces.Size = new System.Drawing.Size(675, 185);
+ this.listHandleTraces.TabIndex = 1;
+ this.listHandleTraces.UseCompatibleStateImageBehavior = false;
+ this.listHandleTraces.View = System.Windows.Forms.View.Details;
+ this.listHandleTraces.SelectedIndexChanged += new System.EventHandler(this.listHandleTraces_SelectedIndexChanged);
+ //
+ // columnIndex
+ //
+ this.columnIndex.Text = "Index";
+ //
+ // columnHandle
+ //
+ this.columnHandle.Text = "Handle";
+ //
+ // columnType
+ //
+ this.columnType.Text = "Type";
+ this.columnType.Width = 120;
+ //
+ // columnTid
+ //
+ this.columnTid.Text = "TID";
+ //
+ // columnHandleName
+ //
+ this.columnHandleName.Text = "Handle Name";
+ this.columnHandleName.Width = 300;
+ //
+ // buttonSnapshot
+ //
+ this.buttonSnapshot.FlatStyle = System.Windows.Forms.FlatStyle.System;
+ this.buttonSnapshot.Location = new System.Drawing.Point(168, 6);
+ this.buttonSnapshot.Name = "buttonSnapshot";
+ this.buttonSnapshot.Size = new System.Drawing.Size(75, 23);
+ this.buttonSnapshot.TabIndex = 0;
+ this.buttonSnapshot.Text = "Snapshot";
+ this.buttonSnapshot.UseVisualStyleBackColor = true;
+ this.buttonSnapshot.Click += new System.EventHandler(this.buttonSnapshot_Click);
+ //
+ // buttonDisableHandleTracing
+ //
+ this.buttonDisableHandleTracing.FlatStyle = System.Windows.Forms.FlatStyle.System;
+ this.buttonDisableHandleTracing.Location = new System.Drawing.Point(87, 6);
+ this.buttonDisableHandleTracing.Name = "buttonDisableHandleTracing";
+ this.buttonDisableHandleTracing.Size = new System.Drawing.Size(75, 23);
+ this.buttonDisableHandleTracing.TabIndex = 0;
+ this.buttonDisableHandleTracing.Text = "Disable";
+ this.buttonDisableHandleTracing.UseVisualStyleBackColor = true;
+ this.buttonDisableHandleTracing.Click += new System.EventHandler(this.buttonDisableHandleTracing_Click);
+ //
+ // buttonEnableHandleTracing
+ //
+ this.buttonEnableHandleTracing.FlatStyle = System.Windows.Forms.FlatStyle.System;
+ this.buttonEnableHandleTracing.Location = new System.Drawing.Point(6, 6);
+ this.buttonEnableHandleTracing.Name = "buttonEnableHandleTracing";
+ this.buttonEnableHandleTracing.Size = new System.Drawing.Size(75, 23);
+ this.buttonEnableHandleTracing.TabIndex = 0;
+ this.buttonEnableHandleTracing.Text = "Enable";
+ this.buttonEnableHandleTracing.UseVisualStyleBackColor = true;
+ this.buttonEnableHandleTracing.Click += new System.EventHandler(this.buttonEnableHandleTracing_Click);
+ //
+ // tabHiddenObjects
+ //
+ this.tabHiddenObjects.Controls.Add(this.labelObjectsScanProgress);
+ this.tabHiddenObjects.Controls.Add(this.buttonScanHiddenObjects);
+ this.tabHiddenObjects.Controls.Add(this.listHiddenObjects);
+ this.tabHiddenObjects.Location = new System.Drawing.Point(4, 22);
+ this.tabHiddenObjects.Name = "tabHiddenObjects";
+ this.tabHiddenObjects.Padding = new System.Windows.Forms.Padding(3);
+ this.tabHiddenObjects.Size = new System.Drawing.Size(687, 443);
+ this.tabHiddenObjects.TabIndex = 1;
+ this.tabHiddenObjects.Text = "Hidden Objects";
+ this.tabHiddenObjects.UseVisualStyleBackColor = true;
+ //
+ // labelObjectsScanProgress
+ //
+ this.labelObjectsScanProgress.AutoSize = true;
+ this.labelObjectsScanProgress.Location = new System.Drawing.Point(87, 11);
+ this.labelObjectsScanProgress.Name = "labelObjectsScanProgress";
+ this.labelObjectsScanProgress.Size = new System.Drawing.Size(41, 13);
+ this.labelObjectsScanProgress.TabIndex = 4;
+ this.labelObjectsScanProgress.Text = "Ready.";
+ //
+ // buttonScanHiddenObjects
+ //
+ this.buttonScanHiddenObjects.Enabled = false;
+ this.buttonScanHiddenObjects.FlatStyle = System.Windows.Forms.FlatStyle.System;
+ this.buttonScanHiddenObjects.Location = new System.Drawing.Point(6, 6);
+ this.buttonScanHiddenObjects.Name = "buttonScanHiddenObjects";
+ this.buttonScanHiddenObjects.Size = new System.Drawing.Size(75, 23);
+ this.buttonScanHiddenObjects.TabIndex = 3;
+ this.buttonScanHiddenObjects.Text = "Scan";
+ this.buttonScanHiddenObjects.UseVisualStyleBackColor = true;
+ this.buttonScanHiddenObjects.Click += new System.EventHandler(this.buttonScanHiddenObjects_Click);
+ //
+ // listHiddenObjects
+ //
+ this.listHiddenObjects.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.listHiddenObjects.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
+ this.columnObjectType,
+ this.columnObjectId,
+ this.columnObjectInfo});
+ this.listHiddenObjects.FullRowSelect = true;
+ this.listHiddenObjects.HideSelection = false;
+ this.listHiddenObjects.Location = new System.Drawing.Point(6, 35);
+ this.listHiddenObjects.MultiSelect = false;
+ this.listHiddenObjects.Name = "listHiddenObjects";
+ this.listHiddenObjects.Size = new System.Drawing.Size(675, 402);
+ this.listHiddenObjects.TabIndex = 2;
+ this.listHiddenObjects.UseCompatibleStateImageBehavior = false;
+ this.listHiddenObjects.View = System.Windows.Forms.View.Details;
+ //
+ // columnObjectType
+ //
+ this.columnObjectType.Text = "Type";
+ this.columnObjectType.Width = 100;
+ //
+ // columnObjectId
+ //
+ this.columnObjectId.Text = "ID";
+ //
+ // columnObjectInfo
+ //
+ this.columnObjectInfo.Text = "Information";
+ this.columnObjectInfo.Width = 300;
+ //
+ // MainWindow
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(695, 493);
+ this.Controls.Add(this.tabControl);
+ this.Controls.Add(this.menuStrip);
+ this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
+ this.MainMenuStrip = this.menuStrip;
+ this.Name = "MainWindow";
+ this.Text = "Process Analyzer";
+ this.menuStrip.ResumeLayout(false);
+ this.menuStrip.PerformLayout();
+ this.tabControl.ResumeLayout(false);
+ this.tabHandleTracing.ResumeLayout(false);
+ this.tabHiddenObjects.ResumeLayout(false);
+ this.tabHiddenObjects.PerformLayout();
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.MenuStrip menuStrip;
+ private System.Windows.Forms.ToolStripMenuItem analyzerToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem openProcessToolStripMenuItem;
+ private System.Windows.Forms.ToolStripSeparator toolStripMenuItem1;
+ private System.Windows.Forms.ToolStripMenuItem exitToolStripMenuItem;
+ private System.Windows.Forms.TabControl tabControl;
+ private System.Windows.Forms.TabPage tabHandleTracing;
+ private System.Windows.Forms.Button buttonDisableHandleTracing;
+ private System.Windows.Forms.Button buttonEnableHandleTracing;
+ private System.Windows.Forms.Button buttonSnapshot;
+ private System.Windows.Forms.ListView listHandleTraces;
+ private System.Windows.Forms.ColumnHeader columnHandle;
+ private System.Windows.Forms.ColumnHeader columnType;
+ private System.Windows.Forms.ColumnHeader columnTid;
+ private System.Windows.Forms.ListView listHandleStack;
+ private System.Windows.Forms.ColumnHeader columnAddress;
+ private System.Windows.Forms.ColumnHeader columnSymbol;
+ private System.Windows.Forms.ColumnHeader columnHandleName;
+ private System.Windows.Forms.ColumnHeader columnIndex;
+ private System.Windows.Forms.TabPage tabHiddenObjects;
+ private System.Windows.Forms.ListView listHiddenObjects;
+ private System.Windows.Forms.ColumnHeader columnObjectType;
+ private System.Windows.Forms.ColumnHeader columnObjectId;
+ private System.Windows.Forms.ColumnHeader columnObjectInfo;
+ private System.Windows.Forms.Button buttonScanHiddenObjects;
+ private System.Windows.Forms.Label labelObjectsScanProgress;
+ }
+}
+
diff --git a/branches/ph-plugins/ExtraTools/ProcessAnalyzer/MainWindow.cs b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/MainWindow.cs
new file mode 100644
index 000000000..589a54aff
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/MainWindow.cs
@@ -0,0 +1,314 @@
+using System;
+using System.Windows.Forms;
+using ProcessHacker.Common;
+using ProcessHacker.Common.Ui;
+using ProcessHacker.Native;
+using ProcessHacker.Native.Api;
+using ProcessHacker.Native.Objects;
+using ProcessHacker.Native.Security;
+using ProcessHacker.Native.Symbols;
+using ProcessHacker.Native.Ui;
+
+namespace ProcessAnalyzer
+{
+ public partial class MainWindow : Form
+ {
+ private int _pid;
+
+ public MainWindow()
+ {
+ InitializeComponent();
+
+ try
+ {
+ KProcessHacker.Instance = new KProcessHacker();
+ }
+ catch
+ { }
+
+ Win32.LoadLibrary("C:\\Program Files\\Debugging Tools for Windows (x86)\\dbghelp.dll");
+
+ listHandleTraces.ListViewItemSorter = new SortedListViewComparer(listHandleTraces);
+ }
+
+ private void openProcessToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ this.ChooseProcess();
+ }
+
+ private void exitToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ this.Close();
+ }
+
+ private void ShowException(string operation, Exception ex)
+ {
+ MessageBox.Show(operation + ": " + ex.Message, "Process Analyzer", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+
+ #region Handle Tracing
+
+ private SymbolProvider _symbols;
+ private ProcessHandleTraceCollection _currentHtCollection;
+
+ private void ChooseProcess()
+ {
+ ChooseProcessDialog cpd = new ChooseProcessDialog();
+
+ if (cpd.ShowDialog() == DialogResult.OK)
+ _pid = cpd.SelectedPid;
+ }
+
+ private void PopulateHandleTraceList()
+ {
+ listHandleTraces.BeginUpdate();
+ listHandleTraces.Items.Clear();
+
+ for (int i = 0; i < _currentHtCollection.Count; i++)
+ {
+ var trace = _currentHtCollection[i];
+ ListViewItem item = new ListViewItem(
+ new string[]
+ {
+ i.ToString(),
+ "0x" + trace.Handle.ToString("x"),
+ trace.Type.ToString(),
+ trace.ClientId.ThreadId.ToString()
+ });
+
+ item.Tag = i;
+ listHandleTraces.Items.Add(item);
+ }
+
+ listHandleTraces.EndUpdate();
+ }
+
+ private void buttonEnableHandleTracing_Click(object sender, EventArgs e)
+ {
+ try
+ {
+ using (var phandle = new ProcessHandle(_pid, ProcessAccess.SetInformation))
+ phandle.EnableHandleTracing();
+ }
+ catch (Exception ex)
+ {
+ this.ShowException("Error enabling handle tracing", ex);
+ }
+ }
+
+ private void buttonDisableHandleTracing_Click(object sender, EventArgs e)
+ {
+ try
+ {
+ using (var phandle = new ProcessHandle(_pid, ProcessAccess.SetInformation))
+ phandle.DisableHandleTracing();
+ }
+ catch (Exception ex)
+ {
+ this.ShowException("Error disabling handle tracing", ex);
+ }
+ }
+
+ private void buttonSnapshot_Click(object sender, EventArgs e)
+ {
+ try
+ {
+ using (var phandle = new ProcessHandle(_pid, ProcessAccess.QueryInformation | ProcessAccess.VmRead))
+ {
+ _currentHtCollection = phandle.GetHandleTraces();
+
+ if (_symbols != null)
+ _symbols.Dispose();
+
+ SymbolProvider.Options |= SymbolOptions.DeferredLoads;
+ _symbols = new SymbolProvider(phandle);
+
+ WorkQueue.GlobalQueueWorkItem(new Action(() =>
+ {
+ var symbols = _symbols;
+
+ _symbols.PreloadModules = true;
+
+ try
+ {
+ foreach (var module in phandle.GetModules())
+ {
+ try
+ {
+ symbols.LoadModule(module.FileName, module.BaseAddress);
+ }
+ catch
+ { }
+ }
+ }
+ catch
+ { }
+
+ try
+ {
+ foreach (var module in Windows.GetKernelModules())
+ {
+ try
+ {
+ symbols.LoadModule(module.FileName, module.BaseAddress);
+ }
+ catch
+ { }
+ }
+ }
+ catch
+ { }
+ }));
+ }
+
+ this.PopulateHandleTraceList();
+ }
+ catch (Exception ex)
+ {
+ this.ShowException("Error getting the handle trace snapshot", ex);
+ }
+ }
+
+ private void listHandleTraces_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ if (_currentHtCollection == null || listHandleTraces.SelectedItems.Count != 1)
+ return;
+
+ var trace = _currentHtCollection[(int)listHandleTraces.SelectedItems[0].Tag];
+
+ listHandleStack.BeginUpdate();
+ listHandleStack.Items.Clear();
+
+ foreach (var address in trace.Stack)
+ {
+ ListViewItem item = new ListViewItem();
+
+ item.Text = "0x" + address.ToInt32().ToString("x8");
+ item.SubItems.Add(new ListViewItem.ListViewSubItem(item, _symbols.GetSymbolFromAddress(address.ToUInt64())));
+ listHandleStack.Items.Add(item);
+ }
+
+ listHandleStack.EndUpdate();
+ }
+
+ #endregion
+
+ #region Hidden Objects
+
+ private struct KVars
+ {
+ public IntPtr NonPagedPoolStartAddress;
+ public IntPtr NonPagedPoolSizeAddress;
+ public IntPtr NonPagedPoolStart;
+ public uint NonPagedPoolSize;
+ public IntPtr PsProcessTypeAddress;
+ public IntPtr PsProcessType;
+ public IntPtr PsThreadTypeAddress;
+ public IntPtr PsThreadType;
+ }
+
+ private unsafe KVars GetKVars()
+ {
+ SymbolProvider symbols = new SymbolProvider();
+
+ symbols.LoadModule(Windows.KernelFileName, Windows.KernelBase);
+
+ KVars vars = new KVars();
+
+ vars.NonPagedPoolStartAddress = symbols.GetSymbolFromName("MmNonPagedPoolStart").Address.ToIntPtr();
+ vars.NonPagedPoolSizeAddress = symbols.GetSymbolFromName("MmMaximumNonPagedPoolInBytes").Address.ToIntPtr();
+ vars.PsProcessTypeAddress = symbols.GetSymbolFromName("PsProcessType").Address.ToIntPtr();
+ vars.PsThreadTypeAddress = symbols.GetSymbolFromName("PsThreadType").Address.ToIntPtr();
+
+ int bytesRead;
+
+ KProcessHacker.Instance.KphReadVirtualMemoryUnsafe(
+ ProcessHandle.Current,
+ vars.NonPagedPoolStartAddress.ToInt32(),
+ &vars.NonPagedPoolStart,
+ IntPtr.Size,
+ out bytesRead
+ );
+ KProcessHacker.Instance.KphReadVirtualMemoryUnsafe(
+ ProcessHandle.Current,
+ vars.NonPagedPoolSizeAddress.ToInt32(),
+ &vars.NonPagedPoolSize,
+ sizeof(uint),
+ out bytesRead
+ );
+ KProcessHacker.Instance.KphReadVirtualMemoryUnsafe(
+ ProcessHandle.Current,
+ vars.PsProcessTypeAddress.ToInt32(),
+ &vars.PsProcessType,
+ IntPtr.Size,
+ out bytesRead
+ );
+ KProcessHacker.Instance.KphReadVirtualMemoryUnsafe(
+ ProcessHandle.Current,
+ vars.PsThreadTypeAddress.ToInt32(),
+ &vars.PsThreadType,
+ IntPtr.Size,
+ out bytesRead
+ );
+
+ symbols.Dispose();
+
+ return vars;
+ }
+
+ private unsafe void ScanHiddenObjects()
+ {
+ KVars vars = this.GetKVars();
+ int bytesRead;
+
+ throw new NotSupportedException();
+
+ listHiddenObjects.Items.Clear();
+
+ using (var currentPage = new MemoryAlloc(Windows.PageSize))
+ {
+ for (
+ IntPtr address = vars.NonPagedPoolStart;
+ address.CompareTo(vars.NonPagedPoolStart.Increment(vars.NonPagedPoolSize)) == -1;
+ address = address.Increment(Windows.PageSize)
+ )
+ {
+ try
+ {
+ KProcessHacker.Instance.KphReadVirtualMemoryUnsafe(
+ ProcessHandle.Current,
+ address.ToInt32(),
+ (IntPtr)currentPage,
+ Windows.PageSize,
+ out bytesRead
+ );
+ }
+ catch
+ {
+ continue;
+ }
+
+ for (
+ IntPtr inner = address;
+ inner.CompareTo(address.Increment(Windows.PageSize)) == -1;
+ inner = inner.Increment(8)
+ )
+ {
+ }
+
+ labelObjectsScanProgress.Text = string.Format("Scanned 0x{0:x8}", address.ToInt32());
+ Application.DoEvents();
+ }
+ }
+
+ labelObjectsScanProgress.Text = "Finished.";
+ }
+
+ private void buttonScanHiddenObjects_Click(object sender, EventArgs e)
+ {
+ this.ScanHiddenObjects();
+ }
+
+ #endregion
+ }
+}
diff --git a/branches/ph-plugins/ExtraTools/ProcessAnalyzer/MainWindow.resx b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/MainWindow.resx
new file mode 100644
index 000000000..0a2236b3e
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/MainWindow.resx
@@ -0,0 +1,912 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 17, 17
+
+
+
+
+ AAABAA8AMDAQAAEABABoBgAA9gAAACAgEAABAAQA6AIAAF4HAAAQEBAAAQAEACgBAABGCgAAAAAAAAEA
+ CABqDQAAbgsAADAwAAABAAgAqA4AANgYAAAgIAAAAQAIAKgIAACAJwAAEBAAAAEACABoBQAAKDAAAAAA
+ AAABABgAOQ0AAJA1AAAwMAAAAQAYAKgcAADJQgAAICAAAAEAGACoDAAAcV8AABAQAAABABgAaAMAABls
+ AAAAAAAAAQAgAHANAACBbwAAMDAAAAEAIACoJQAA8XwAACAgAAABACAAqBAAAJmiAAAQEAAAAQAgAGgE
+ AABBswAAKAAAADAAAABgAAAAAQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAIAAAACA
+ gACAAAAAgACAAICAAACAgIAAAAD/AAD/AAAA//8A/wAAAP8A/wD//wAAwMDAAP///wDwAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFRGVGVlZGVkdGVlAAAA
+ AAAAAAAAAAAAAFZGtkZLa2Rka0a0AAAAAAAAAAAAAAAAAGtka2tka2trZkZHAAAAAAAAAAAAAAAAAGRr
+ a2tmtmtra2tkAAAAAAAAAAAAAAAAAFZrZma2a2a2a2tnAAAAAAAAAAAAAAAAAEa2a2tra2tra2ZkAAAA
+ AAAAAAAAAAAAAGa2trZra2a2a2tlAAAAAAAAAAAAAAAAAHa2bbZrZttmtmtmAAAAAAABODE4ExgxAFa2
+ tra2tra2tmtlAAAAAAABgxg4E4OBAEZrZrZr1rZr29tmAAAAAAADgTETgxMTAHvWvb22tmvba2tlAAAA
+ AAADE4ODgxg4AGRr272729tr29vUAAAAAAAIE4MTgTgxAF2729vb22bb29tnAAAAAAABODg4ODgxAEbb
+ 29vb29u2vb22AAAAAAADg4ODg4ODAEZmZmZmZm1mZmZlAAAAAAABODg4ODg4AHR2VlZWR1ZHRlZWAAAA
+ AAAIODg4ODg4AAAAAAAAAAAAAAAAAAAAAAADg4ODg4ODM4ODiDiDg4ODg4OIOIODg44BODiDioOBiuiu
+ p6euinqK6K6np66o6j4BioODg4ODOurqjq6nrq6urqeup3qK6h4Dg4OKg4ODjoruqK6o6o6o6uqOqurq
+ 6o4Bg4ODiDg4Oq6orqeup66np6iuqOqOqD4Dg4qIOKg4h6eup66Kenp6eurqeup66j4Biog4qDiDPqen
+ p66urq6np66K6np6eo4DiKg4OKiBiq6nrqiuqKeup6p6enp66j4Bg4OIODgzOup66nrqeup6eurqenrq
+ eo4BMRMTgTGBh66orqenp6rorop66np66j4AAAAAAAAAOup66np66nrqrqrqenrqeo4AAAAAAAAAinp6
+ eup6enrqeurqeup66j4AAAAAAAAAPqrq6qeq6up6euqK6q6uqh4AAAAAAAAAiup6eup6eqeup66urqiu
+ 6j4AAAAAAAAAOup66np66n6qeqeqenrqqo4AAAAAAAAAGq6q6q6q6qrq6urqrqrq6j4AAAAAAAAAPq6u
+ qurq6urq6uqurq6q6o4AAAAAAAAAiq6q6uqq6q6qququqq6q6j4AAAAAAAAAOuqurqrq6uqurq6urq6u
+ ro4AAAAAAAAAiurqqurq6q6urqrqrqquqj4AAAAAAAAAPq6q6urqququqq6q6q6uro4AAAAAAAAAOqrq
+ 6qqurq6q6urqrq6q6j4AAAAAAAAAOurqqurqrqququqq6uquqj4AAAAAAAAAiuqurqrqrq6q6q6uqq6q
+ 6o4AAAAAAAAAOuququrqrqrq6q6q6uquqn4AAAAAAAAAeq6uququrqrqrq6q6q6uqo4AAAAAAAAAOq6q
+ rqquqq6qrqquqq6qrj4AAAAAAAAAgzODM4MzgzODM4MzgzODMT8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AA9///////4AAP///////wAA//8AAAP/AAD//wAAA/8AAP//AAAD/wAA//8AAAP/AAD//wAAA/8AAP//
+ AAAD/wAA//8AAAP/AAD//wAAA/8AAIADAAAD/wAAgAMAAAP/AACAAwAAA/8AAIADAAAD/wAAgAMAAAP/
+ AACAAwAAA/8AAIADAAAD/wAAgAMAAAP/AACAA/////8AAIAAAAAAAAAAgAAAAAAAAACAAAAAAAAAAIAA
+ AAAAAAAAgAAAAAAAAACAAAAAAAAAAIAAAAAAAAAAgAAAAAAAAACAAAAAAAAAAIAAAAAAAAAA//wAAAAA
+ AAD//AAAAAAAAP/8AAAAAAAA//wAAAAAAAD//AAAAAAAAP/8AAAAAAAA//wAAAAAAAD//AAAAAAAAP/8
+ AAAAAAAA//wAAAAAAAD//AAAAAAAAP/8AAAAAAAA//wAAAAAAAD//AAAAAAAAP/8AAAAAAAA//wAAAAA
+ AAD//AAAAAAAAP/8AAAAAAAA///////+AAAoAAAAIAAAAEAAAAABAAQAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAgAAAgAAAAICAAIAAAACAAIAAgIAAAICAgAAAAP8AAP8AAAD//wD/AAAA/wD/AP//
+ AADAwMAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVlZWVkZWVAAAAAAAAAAAAGRra2tGS2TgAAAAAA
+ AAAABWtrZrZrZl4AAAAAAAAAAAZrZra2trZHAAAAAAAAAAAFa2trZmtmTgAAAAAAAAAABrZra2tmtk4A
+ AADhOBMYMAVmtmZrbbZ+AAAA8Tg4MTcGtr2727a2RwAAAOGDE4OOBW1r29vb224AAADhODgxjgRrZmZm
+ ZmZOAAAA6Dg4OD4FZWVlZUZWdwAAAOODg4OOAAAAAAAAAAAAAADhg4OIMziIg4g4iDiIODg+44OKg4Gn
+ p66np6enp6jqPug4g4g4rqenqOp6enrqeo7xo4qIOHp6eurq6up6euo34YiDgxOup6enqK6K6np6h+MT
+ ETgYp66nqueqenp66j4AAAAAA66np656p+p66nqOAAAAAAGnrqeqfqp6enrqNwAAAAAD6uqurqqurq6q
+ 6ocAAAAACK6urqrq6q6q6uo+AAAAAAOuququ6urqrq6qjgAAAAAIququqqrq6uqq6jcAAAAAA+rq6q6u
+ rqqurq6OAAAAAAOq6q+uqqrq6q6qjgAAAAADrqrqqq6uququrj4AAAAAA66q6urqrqrq6qqHAAAAAAiu
+ quqq6q6q6qrqPgAAAAADODODgzg4M4ODOD4AAAAADu7u7u7u7u7u7u7v///////gAH//4AA//+AAP//g
+ AD//4AA//+AAPwBgAD8AIAA/ACAAPwAgAD8AIAA/AD///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+A
+ AAD/gAAA/4AAAP+AAAD/gAAA/4AAAP+AAAD/gAAA/4AAAP+AAAD/gAAA/4AAAP+AAAAoAAAAEAAAACAA
+ AAABAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAgAAAAICAAIAAAACAAIAAgIAAAICA
+ gAAAAP8AAP8AAAD//wD/AAAA/wD/AP//AADAwMAA////AAAAAAAAAAAAAAAHu2a2cAAAAAdmtrtwAOc3
+ B2a2ZnAAeDh+tr22cAB4OD5HZWVwAOg4Pn7qfn6ueIOKp6jqend6iIeup6enp+eOPqenrqenAAAK6qen
+ rqcAAArq6uqupwAADqrqrqrqAAAKrqrq6uoAAAqq6qqqpwAADu7q7u7u//8AAPgHAAD4BwAACAcAAAAH
+ AAAABwAAAAAAAAAAAAAAAAAAAAAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAIlQTkcNChoKAAAADUlI
+ RFIAAAEAAAABAAgGAAAAXHKoZgAADTFJREFUeNrt3QuoZVUZwPF9zrlN06SNltWYSoVMSFEUex5mvp2X
+ j6QiiEqskIysDIQeCBUVSA8QsjIypBIrgqgwdRrH16hNzovCKKRBKtTJzNTRaZoZz6Oz5t59717n7n1n
+ P9baa317/X+g39lnUPZ5/Wfts8+5txMBCFZH/WvnztHI9Y4AaNaKFZ3ObACWLnW9OwCasndvFKnXvBaA
+ 5cungwCg3dRrfvFiAgAESb3mp6YIABAkAgAEjAAAASMANfzqgxGnTSHSu38y95onABURAEhFAAwgAJCK
+ ABhAACAVATCAAEAqAmBAVgAu/u4a17sFzHPLJ+7UtgmAAQQAUhAACwgApCAAFhAASEEALCAAkIIAWEAA
+ IAUBsIAAQAoCYEFWAN553Zqo03O9Z4COAFiQtwIYDaLDEWAyfZm/uZIAGJe3AgB8QwAsIACQggBYQAAg
+ BQGwgABACgJgAQGAFATAgrwADPudqDs1YjK9mAoBsIAVACRQIbjtqs3adQTAgKwAXHjtWte7BcxDACzI
+ C8Dk8gto2uRhAIcAFiy0AkjufKBp6edecpkVgAUcAkAKAmABAYAUBMACAgApCIAFBABSEAALCACkIAAW
+ EABIQQAsyAvAsK9Ov0SHp8JlLru+vPGzBMC4hQKguH7QuczlBAGwICsA538j+SDQ+AE4NJ6LUjP1gGh/
+ zvVcb+l6ta1wCGDBQgHINBz/0+V6rm/o+hRWABaUDoAynFsVaA8a13O9jetn/mzj5wmAcZkB+FpOANSD
+ MnS9xwhSlxWAFZVWAIADBMACAgAROASwo9QhAOAQAbCAAEAKAmABAYAUBMACAgApCIAFWQFYf83aqNuN
+ oiGn/OCRTVcTAONYAUAC9ZcRAbAgbwUA+EStSDkEsIAAQApWABYQAEhBACzIC8CoP75Dx3cqk+nDVAiA
+ BawAIAUBsCArAOu+QgDgnzu+SACMWygAPiz9mMxkEgALWAFAAhUBDgEsIACQghWABQQAUhAACwgApCAA
+ FhAA+G406ESd3ogA2JAXAHWnAz7Z/OU7tG0CYEBWANZ+ad3h4iblZTJdT4UAWJAXACV95wOuJM9DAmDB
+ QgEAfEIALCAAkIIAWEAAIAUBsIAAQAoCYEFWAM77wrrZHwrKZPowlbu+SgCMWygAig8PPpOpJgGwIC8A
+ gG8IgAUEAFIQAAsIAKQgABYQAEhBACzIC8BooD6CGTGZXkyFAFiQFYBzr2YFAP/cfQ0BMI4AQAoCYAEB
+ gBQEwAICACkIgAUEAFIQAAuyAnDO5wgA/HPP1wmAcQsFwIfTP0xmMgmABawAIAUBsIAAQAoCYAEBgBQE
+ wAICACkIgAUEAFIQAAuyAnD2ZwgA/HPvNwmAcQQAUhAACwgApCAAFhAASEEALCAAkIIAWJAXgOEgirq9
+ iMn0YioEwAICwJQy77uWABiXFYAzr+IQAP4hABYQAEhBACzICsAZn17vereAWZ3e9FOUAFiw0ApAHXsN
+ DnWi3qIRk+l83v+tTdrzlAAYwAoAUhAACxYKgOviM5lGVgA/juc/yRFFH9oVdVgBQAL1PkDl9wAIQDYC
+ AElYARhGACAJATCMAEASAmAYAYAkRgNw1kmub07ztjyqbxMASGI2ACe6vjnN2/KYvp0XgNM/RQDgnwe+
+ TQBqIQCQzGgATj/B9c1p3gOP69sEAJIQgJq27omiYeqeIACQhADUVHQF8I4rCAD887vrCUAtZQPg+rfB
+ MpnpSQBqYgUAyYwG4NTjp+dUN4r6wzAmAYBkxlcA6oURkgf/qW8TAEhiZQWg+PC3cxNz+xPFzgIQAPjI
+ WAC64z9Ztcz1zWkeKwBIZnQFsOp4fXv8l2Q0bPlUAVDxS1YBBACSGF0BrAhwBbC9xApg2B/fT+M7lcn0
+ YSpWVwAhKBqAt1/OCgD++f0NBgOw4tXjsnRd36RmDGfOduz8l349hwCQQK0CFKMBUCuAYUCnAVXsthd8
+ D0CtAJLlF+AL4yuA0BRdAXAIAB8RgJoIACQjADURAEhmNABvfaXrm9O8P/5b3yYAkIQVQEXJdx6KBuDU
+ ywgA/JF8HfjBG1kB1PLQU8W+C5AEwPX3v5nM9DQWAHUq7E3HRVHP9SuyYWUOAXx4wJlMNRV1mfcAaiq7
+ AlBGw07U6Y6YTKdTMXoI8ObAAqBWO7wHAGnU3/zDF6YjQABq+nPBFcDqj2xwvavArGQlsO2Hv9WuJwAl
+ /angCiAJQLL0AlxQL/o0AlBT2QAAPjEaAHUWIDTqECCNAEASAlATAYBkBKAmAgDJCEBNBACSGQ3AKS93
+ fXOa9/DT+nbRAAwHUdTtMZluJwGoqWgAVl66YfaOT/jwBGCGPQlABYPxLe/N3BNlAqBMRgBwacdNBgOw
+ /FjXN6c5KgAqBLuf0a8/UgAAnxgLgPo24MnHzL0w2j4TBACSsQKo6ZFni30XYOUlGyIvfpURk5maBKCm
+ UisATx50ZuAzmrk8tuNmgwFQhwChUSuAIr8X4PAKAPAMAahJBSAtLwCABASgJAKANiEAJREAtImRAAyG
+ UdTrhjH//px+HxAASFYrAK97mevdb15WAFzvE1AXASiIAKCNCEBBBABtVCsA/fGfTHXCmHv2zf8koOsH
+ D6irUgBOPNr1bjfvsef1bQKANiAABbECQBsRgIJYAaCNKgXghKPG/0FgT38VgMnvAhT57+L4cj4r0EK7
+ dt3QildA5QCE5vF9+jYBCBsBCAwBQFrQAVj2Ute73bwn/qtvE4CwEYDAPLm/2lkAAtBOBCAwJlcAuy66
+ 0vXNQUnxrddp2wQgMCoAps4CEAB5CEBKqAFIIwBhIQApSQD6wyia6oYxn/qffh8YCcBw6PrxR0Hx7d/R
+ toMOwHEvcb3bzTMagAs+OffTRiACAUgJMQBPHzB3FmA2ABAj3nS9tk0AAmN0BbD+Ctc3B2WMV2qsAFJC
+ DYCxswAEQI6ZBzze/D3tagIQGKMrgLUfd31zUBIBSDnmxa53u3nPHtS3aweAMwCixHd9X9sOOgBqBeDD
+ qbmmpmI0AOd9zPXjjjK6XVYAaSGuAJ47ZPAsAAEQhxVASogBMLoCOOejrm8OylArAAIwJ9QAGDsLQADE
+ ie/5gbZNAALDCiBsBCDlqBe53u3m7XtB364VgDMvc31zUFJ8343adrABUMvgJVPT75CHIDkLQADCRgBS
+ 0isA9frotngm9vcNngVIAtAfjEvaYwqY8dYf6Y9hyAFYkgqADy9SGzNNXWd0BXDah6cvJE8y+G38OLEC
+ SFkS4HsAB0yuAJIAQAxWACkhBmC/jRUAxCAAKZMBUB9r77b8TUEVAGOfA1AB4OcBiBJvu0nbJgDR3PdZ
+ 1Iu/rd9tSW7bgYlD9VoBWH2p65uFkghAyuLe/P9RmyOgEICwEYCUrAC0ndEArLzE9c1BGeq7AARgThKA
+ /vhPplpxNxwZAQhbvONmbZsARHMBaPtUjAYg/oDrxx15kmPZiXe1CUBKKB8DTutPvL9BAAKhTvt0O1G8
+ 66fa1cEGQJ0K67bippdjNABve7/rm4MyCICOFQABCE38h59p2wQgIGoVOJy4FwhAyyW/uWlmEoCUEA8B
+ Zg4FzXwSUAWATwKKEj/0c2076ACgZgDe8j7Xu4+SCAA0tQOQfNc84cF33pn5kwBAYyQACQ+e4MyMmXp8
+ CAA0tQLwxve63n2UFP/lF9p2MAFAPUcMQPKpM6Zfc/JxJACoIjcAIXyXukXih3+pbRMAFJIZgFPe43q3
+ UIb6NiArAFRBANqBFQAqyQzAG97lerdQhloBEABUQQDaIf7rr7VtAoBCCEA7EABUkhmAky92vVsoKX7k
+ Fm2bAKCQzAC8/iI/znUzC38mgACgktwVAOf+RYn/dqu2TQBQSO4KAKIQAFSSG4AhX7GQJP7Hbdo2AUAh
+ mQF47YWudwslEQBUkhsA3gMQJX50o7ZNAFBIZgBOOt/1bqGMQSeK99yuXUUAUEhmAF5zwcwl9UMnekzv
+ Z0QAUE12ANa73i2U0iMAqCZ/BTCo8H+DK/GeTdo2AUAhmQFYts71bqEM9ZuBCACqIADCzZytiZ+8U7ua
+ AKCQzAC8ao3r3UJJBACVEIB2IACoJDcAE797jun3JACoJDMArzh37skFEeL/3K1tEwAUkhsA199xZx55
+ KsnPAyAAqGLBAECM+Jl7tW0CgEIyA3Ds2a53CyURAFSSGwB+HoAo8d4t2jYBQCGZAVh6luvdQkkEAJXk
+ BoD3AESJn79f2yYAKCQzAEef4Xq3UIb6zUCsAFAFAWgHVgCoJDMAS05zvVsoqjfzA0EIAKogAO0Q79+q
+ bRMAFEIA2oEAoBIC0A4EAJXkBqDPjwQTY6pHAFBNZgAWrXa9WygpPrRN2yYAKIQAtAMBQCW5AeC7AKLE
+ /e3aNgFAIZkBmFrlerdQEgFAJVkBgHwEAIUQgHYiACiEALQTAUAhBKCdCAAA8QgAEDACAASMAAABIwBA
+ wAgAEDACAASMAAABmxcA1zsEoFmzAVB27x6NDh6Mon7f9W4BaMr/AZCxqA55eVu6AAAAAElFTkSuQmCC
+ KAAAADAAAABgAAAAAQAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmMzMAmjkDAJ46AACePgMA
+ oj8AAKJCAwCmQwAApkcDAKpJAACpTAMArk4AAK1RAwCyUwAAsVYDALVZAAC5XQAAvmIAALxkAwDBaAAA
+ w24DAMZtAADHcwMAynIAAMt3AwDOdwAA0nwAAAAzoQAANKMAADSkAAA2qQAAOa4AADqyAAA8tQAAPbkA
+ AD+8AABbrwAAQL4AAEHBAABDxQAARMYAAEXJAABGzAAASM8AAEjQAABK1AAATNkASLnkAEi95gBHwugA
+ R8XqAEfI6wBHyuwARs3tAEbR7wBG0/AARtbyAEba9ABF3fUAReD3AEXj+ABF5vkAROn7AETt/ABE8P4A
+ rLzZALrH3wDl2eIA/uHhAACwNgAAz0AAAPBKABH/WwAx/3EAUf+HAHH/nQCR/7IAsf/JANH/3wD///8A
+ AAAAAAIvAAAEUAAABnAAAAiQAAAKsAAAC88AAA7wAAAg/xIAPf8xAFv/UQB5/3EAmP+RALX/sQDU/9EA
+ ////AAAAAAAULwAAIlAAADBwAAA9kAAATLAAAFnPAABn8AAAeP8RAIr/MQCc/1EArv9xAMD/kQDS/7EA
+ 5P/RAP///wAAAAAAJi8AAEBQAABacAAAdJAAAI6wAACpzwAAwvAAANH/EQDY/zEA3v9RAOP/cQDp/5EA
+ 7/+xAPb/0QD///8AAAAAAC8mAABQQQAAcFsAAJB0AACwjgAAz6kAAPDDAAD/0hEA/9gxAP/dUQD/5HEA
+ /+qRAP/wsQD/9tEA////AAAAAAAvFAAAUCIAAHAwAACQPgAAsE0AAM9bAADwaQAA/3kRAP+KMQD/nVEA
+ /69xAP/BkQD/0rEA/+XRAP///wAAAAAALwMAAFAEAABwBgAAkAkAALAKAADPDAAA8A4AAP8gEgD/PjEA
+ /1xRAP96cQD/l5EA/7axAP/U0QD///8AAAAAAC8ADgBQABcAcAAhAJAAKwCwADYAzwBAAPAASQD/EVoA
+ /zFwAP9RhgD/cZwA/5GyAP+xyAD/0d8A////AAAAAAAvACAAUAA2AHAATACQAGIAsAB4AM8AjgDwAKQA
+ /xGzAP8xvgD/UccA/3HRAP+R3AD/seUA/9HwAP///wAAAAAALAAvAEsAUABpAHAAhwCQAKUAsADEAM8A
+ 4QDwAPAR/wDyMf8A9FH/APZx/wD3kf8A+bH/APvR/wD///8AAAAAABsALwAtAFAAPwBwAFIAkABjALAA
+ dgDPAIgA8ACZEf8ApjH/ALRR/wDCcf8Az5H/ANyx/wDr0f8A////AAAAAAAIAC8ADgBQABUAcAAbAJAA
+ IQCwACYAzwAsAPAAPhH/AFgx/wBxUf8AjHH/AKaR/wC/sf8A2tH/AP///wBEAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQIDAwMDAwMDAwMDAwMDAwMDAwMDAQAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAQQFBQUFBQUFBQUFBQUFBQUFBQUFAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AQYHBwcHBwcHBwcHBwcHBwcHBwcHAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQgJCQkJCQkJCQkJ
+ CQkJCQkJCQkJAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQoMlZWVlZWVlZWVlZWVlZWVlZWVAQAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQwNDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NAQAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAQ4PEA8PDw8PDw8PDw8PDw8PDw8PAQAAAAAAAAAAAAAAGxsbGxsbGxsbGxsbHAAA
+ AQ8QEBAQEBAQEBAQEBAQEBAQEBAQAQAAAAAAAAAAAAAAGxwdHR0dHR0dHR0dGwAAARASERERERERERER
+ ERERERERERERAQAAAAAAAAAAAAAAHB4eHh4eHh4eHh4eGwAAARITExMTExMTExMTExMTExMTExMTAQAA
+ AAAAAAAAAAAAGx4eHh8eHx4fHh8fGwAAARMVFRUVFRUVFRUVFRUVFRUVFRUVAQAAAAAAAAAAAAAAGx8f
+ Hx8fHx8fHx8fGwAAARQXFxcXFxcXFxcXFxcXFxcXFxcXAQAAAAAAAAAAAAAAGyAhISEhISEhISEgGwAA
+ ARYZGRkZGRkZGRkZGRkZGRkZGRkZAQAAAAAAAAAAAAAAGyEhISEhISEhISEhGwAAARgaGhoaGhoaGhoa
+ GhoaGhoaGhoaAQAAAAAAAAAAAAAAGyIiIyIjIyIjIyMiGwAAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAA
+ AAAAAAAAAAAAGyUlJSUlJSUlJSUlGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGyYm
+ JiYmJiYmJiYmGyQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJEIAGyYnJygnKCcoJycnHCQv
+ Ly8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vJEEAGygpKSkpKSkpKSkpHCQwLzAwMDAwMDAwMDAw
+ MDAwMDAwMDAwMDAwMDAwMDAvJEEAGykrKiorKisqKyoqHCQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw
+ MDAwMDAwJEEAGyosLCwsLCwsLCwsGyQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwJEEAGywt
+ LS0tLS0tLS0tHCQxMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExJEEAGy0uLi4uLi4uLi4uHCQy
+ MjIxMjEyMTIxMjEyMTIxMjEyMTIxMjEyMTIxMjEyJEEAGy4uLi4uLi4uLi4uGyQyMjIyMjIyMjIyMjIy
+ MjIyMjIyMjIyMjIyMjIyMjIyJEEAGyorKysrKysrKysrGyQzMzQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0
+ NDQ0NDQzJEEAGxsbGxsbGxsbGxsbHCQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDU0JEEAAAAA
+ AAAAAAAAAAAAACQ1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1JEEAAAAAAAAAAAAAAAAAACQ1
+ NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1JEEAAAAAAAAAAAAAAAAAACQ2Njc3Nzc3Nzc3Nzc3
+ Nzc3Nzc3Nzc3Nzc3Nzc3NzY2JEEAAAAAAAAAAAAAAAAAACQ3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3
+ Nzc3Nzg3JEEAAAAAAAAAAAAAAAAAACQ4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4JEEAAAAA
+ AAAAAAAAAAAAACQ4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4JEEAAAAAAAAAAAAAAAAAACQ5
+ OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5JEEAAAAAAAAAAAAAAAAAACQ6Ojo6Ojo6Ojo6Ojo6
+ Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6JEEAAAAAAAAAAAAAAAAAACQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7
+ Ozs7Ozs7JEEAAAAAAAAAAAAAAAAAACQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7JEEAAAAA
+ AAAAAAAAAAAAACQ8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8JEEAAAAAAAAAAAAAAAAAACQ9
+ PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09JEEAAAAAAAAAAAAAAAAAACQ9PT09PT09PT09PT09
+ PT09PT09PT09PT09PT09PT09JEEAAAAAAAAAAAAAAAAAACQ+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+
+ Pj4+Pj4+JEEAAAAAAAAAAAAAAAAAACQ/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/JEEAAAAA
+ AAAAAAAAAAAAACQ/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/JEEAAAAAAAAAAAAAAAAAACRA
+ QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAJEEAAAAAAAAAAAAAAAAAACQkJCQkJCQkJCQkJCQk
+ JCQkJCQkJCQkJCQkJCQkJCQkJEMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAER///////4O7v///////w7u//8AAAP/Du7//wAAA/8O7v//AAAD/w7u//8AAAP/Du7//wAA
+ A/8O7v//AAAD/w7u//8AAAP/Du7//wAAA/8O7oADAAAD/w7ugAMAAAP/Du6AAwAAA/8O7oADAAAD/w7u
+ gAMAAAP/Du6AAwAAA/8O7oADAAAD/w7ugAMAAAP/Du6AA/////8O7oAAAAAAAA7ugAAAAAAADu6AAAAA
+ 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
+ HBwcHBwcHBwcHBwcHBwcHBwcEBktCxYWFhYWFgsQHR0dHR0dHR0dHR0dHR0dHR0dHR0QGS0LCwsLCwsL
+ CxAeHh4eHh4eHh4eHh4eHh4eHh4eHhAZAAAAAAAAAAAAEB8fHx8fHx8fHx8fHx8fHx8fHx8fEBkAAAAA
+ AAAAAAAQICAgICAgICAgICAgICAgICAgICAQGQAAAAAAAAAAABAhISEhISEhISEhISEhISEhISEhIRAZ
+ AAAAAAAAAAAAECIiIiIiIiIiIiIiIiIiIiIiIiIiEBkAAAAAAAAAAAAQIyMjIyMjIyMjIyMjIyMjIyMj
+ IyMQGQAAAAAAAAAAABAkJCQkJCQkJCQkJCQkJCQkJCQkJBAZAAAAAAAAAAAAECUkJSQlJCQkJCQkJCQk
+ JCQkJCQkEBkAAAAAAAAAAAAQJSUlJSUlJSUlJSUlJSUlJSUlJSUQGQAAAAAAAAAAABAmJiYmJiYmJiYm
+ JiYmJiYmJiYmJhAZAAAAAAAAAAAAECcnJycnJycnJycnJycnJycnJycnEBkAAAAAAAAAAAAQKCgoKCgo
+ KCgoKCgoKCgoKCgoKCgQGQAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQEBArAAAAAAAAAAAALy4u
+ Li4uLi4uLi4uLi4uLi4uLi4uLjD//////+AAf//gAD//4AA//+AAP//gAD//4AA/AGAAPwAgAD8AIAA/
+ ACAAPwAgAD8AP///AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/4AAAP+AAAD/gAAA/4AAAP+AAAD/gAAA
+ /4AAAP+AAAD/gAAA/4AAAP+AAAD/gAAA/4AAACgAAAAQAAAAIAAAAAEACAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAoj8AAK5OAAC6XQAAr2MPAMZtAACqbEwAsHNMALZ7TACwflQAvINMALKDbwC2h28A
+ uYxvAL2RbwC2jnQAADerAAA8tgAQUL8AQGi9AABCwgAAR80AEFPEAABM2AAlW8AAQGrDAD6w3wA+tuEA
+ PrzkAHiGwgB4iMUAcIzKAHiJyQB4jM0AZI3QAHCT2QBftdwAX7neAGCt2ABgstoAR7zmAF+/4QA9wucA
+ PcjqADzO7QA81O8AO9ryAEfD6QBGyuwAXsLhAEbR7wBG2PIARd/1AEXl+ABE7PwAu7zZALu93ACBuuAA
+ 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
+ ADAAAABgAAAAAQAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7i4gAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7g
+ 4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAGYzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2Yz
+ M2YzM2YzM2YzM2YzM2YzM2YzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM5o5A546AJ46AJ46AJ46AJ46AJ46
+ AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AGYzMwAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AGYzM54+A6I/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/
+ AKI/AGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM6JCA6ZDAKZDAKZDAKZDAKZDAKZDAKZDAKZDAKZDAKZD
+ AKZDAKZDAKZDAKZDAKZDAKZDAKZDAKZDAKZDAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM6ZHA6pJAKpJ
+ AKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAGYzMwAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAGYzM6lMA65OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5O
+ AK5OAK5OAK5OAK5OAK5OAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM61RA7JTALJTALJTALJTALJTALJT
+ ALJTALJTALJTALJTALJTALJTALJTALJTALJTALJTALJTALJTALJTAGYzMwAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AGYzM7FWA7ZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZ
+ ALZZAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQAzoQAzoQAzoQAzoQAzoQAz
+ oQAzoQAzoQAzoQAzoQAzoQAzoQAAAAAAAGYzM7RaA7pdALpdALpdALpdALpdALpdALpdALpdALpdALpd
+ ALpdALpdALpdALpdALpdALpdALpdALpdALpdAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAzoQA0owA0pAA0pAA0pAA0pAA0pAA0pAA0pAA0pAA0pAA0pAAzoQAAAAAAAGYzM7hfA75iAL5i
+ AL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAGYzMwAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA2pwA2qAA2qAA2qAA2qAA2qAA2qAA2qAA2qAA2
+ qAA2qAAzoQAAAAAAAGYzM7xkA8JoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJo
+ AMJoAMJoAMJoAMJoAMJoAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA3qwA3
+ qwA3qwA3qwA3qwA3qwA3qwA3qwA3qwA3qwA3qwAzoQAAAAAAAGYzM8BpA8ZtAMZtAMZtAMZtAMZtAMZt
+ AMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAGYzMwAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAzoQA5rgA5rwA5rwA5rwA5rwA5rwA5rwA5rwA5rwA5rwA5rwAzoQAAAAAA
+ AGYzM8NuA8pyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpy
+ AMpyAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA6sQA7swA7swA7swA7swA7
+ swA7swA7swA7swA7swA7swAzoQAAAAAAAGYzM8dzA853AM53AM53AM53AM53AM53AM53AM53AM53AM53
+ AM53AM53AM53AM53AM53AM53AM53AM53AM53AGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAzoQA8tQA8tgA8tgA8tgA8tgA8tgA8tgA8tgA8tgA8tgA8tgAzoQAAAAAAAGYzM8t3A9J8ANJ8
+ ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8AGYzMwAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA9uAA+ugA+ugA+ugA+ugA+ugA+ugA+ugA+ugA+
+ ugA+ugAzoQAAAAAAAGYzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2Yz
+ M2YzM2YzM2YzM2YzM2YzM2YzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA/vABA
+ vgBAvgBAvgBAvgBAvgBAvgBAvgBAvgBAvgBAvgAzoQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAzoQBBvwBBwQBBwQBBwQBBwQBBwQBBwQBBwQBBwQBBwQBBwQAzoQBbrwBb
+ rwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBb
+ rwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbr7rH3wAAAAAzoQBCwwBDxQBDxQBDxQBDxQBD
+ xQBDxQBDxQBDxQBDxQBDxQAzoQBbr0i45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei4
+ 5Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45ABbr6y8
+ 2QAAAAAzoQBExgBFyQBFyQBFyQBFyQBFyQBFyQBFyQBFyQBFyQBFyQAzoQBbr0i65Ui65Ui65Ui65Ui6
+ 5Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui6
+ 5Ui65Ui65Ui65Ui65Ui65Ui65QBbr6y82QAAAAAzoQBFygBHzABHzABHzABHzABHzABHzABHzABHzABH
+ zABHzAAzoQBbr0i85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki8
+ 5ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85gBbr6y82QAAAAAzoQBHzQBI
+ 0ABI0ABI0ABI0ABI0ABI0ABI0ABI0ABI0ABI0AAzoQBbr0i/50i/50i/50i/50i/50i/50i/50i/50i/
+ 50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/
+ 50i/50i/5wBbr6y82QAAAAAzoQBI0QBK1ABK1ABK1ABK1ABK1ABK1ABK1ABK1ABK1ABK1AAzoQBbr0fB
+ 6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB
+ 6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6ABbr6y82QAAAAAzoQBK1ABM2ABM2ABM2ABM2ABM
+ 2ABM2ABM2ABM2ABM2ABM2AAzoQBbr0fD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD
+ 6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6QBbr6y8
+ 2QAAAAAzoQBM2ABN2wBN2wBN2wBN2wBN2wBN2wBN2wBN2wBN2wBN2wAzoQBbr0fF6kfF6kfF6kfF6kfF
+ 6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF
+ 6kfF6kfF6kfF6kfF6kfF6kfF6gBbr6y82QAAAAAzoQBGzABIzwBIzwBIzwBIzwBIzwBIzwBIzwBIzwBI
+ zwBIzwAzoQBbr0fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI
+ 60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI6wBbr6y82QAAAAAzoQAzoQAz
+ oQAzoQAzoQAzoQAzoQAzoQAzoQAzoQAzoQAzoQAzoQBbr0fK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK
+ 7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK
+ 7EfK7EfK7ABbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0fM
+ 7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM
+ 7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7QBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAABbr0bO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO
+ 7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7gBbr6y8
+ 2QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0bR70bR70bR70bR70bR
+ 70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR
+ 70bR70bR70bR70bR70bR70bR7wBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAABbr0bT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT
+ 8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8ABbr6y82QAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0bV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV
+ 8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV
+ 8kbV8kbV8gBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0bX
+ 80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX
+ 80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX8wBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAABbr0ba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba
+ 9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9ABbr6y8
+ 2QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xc9UXc9UXc9UXc9UXc
+ 9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc
+ 9UXc9UXc9UXc9UXc9UXc9UXc9QBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAABbr0Xe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe
+ 9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9gBbr6y82QAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg
+ 90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg
+ 90Xg90Xg9wBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xj
+ +EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj
+ +EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+ABbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl
+ +UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+QBbr6y8
+ 2QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xn+kXn+kXn+kXn+kXn
+ +kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn
+ +kXn+kXn+kXn+kXn+kXn+kXn+gBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAABbr0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp
+ +0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+wBbr6y82QAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Ts/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs
+ /ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs
+ /ETs/ETs/ABbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Tu
+ /UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu
+ /UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/QBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAABbr0Tw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw
+ /kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/gBbr669
+ 2QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbrwBbrwBbrwBbrwBbrwBb
+ rwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBb
+ rwBbrwBbrwBbrwBbrwBbrwBbrwBbr+XZ4gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7i4n///////g7u////
+ ////Du7//wAAA/8O7v//AAAD/w7u//8AAAP/Du7//wAAA/8O7v//AAAD/w7u//8AAAP/Du7//wAAA/8O
+ 7v//AAAD/w7ugAMAAAP/Du6AAwAAA/8O7oADAAAD/w7ugAMAAAP/Du6AAwAAA/8O7oADAAAD/w7ugAMA
+ AAP/Du6AAwAAA/8O7oAD/////w7ugAAAAAAADu6AAAAAAAAO7oAAAAAAAA7ugAAAAAAADu6AAAAAAAAO
+ 7oAAAAAAAA7ugAAAAAAADu6AAAAAAAAO7oAAAAAAAA7ugAAAAAAADu7//AAAAAAO7v/8AAAAAA7u//wA
+ AAAADu7//AAAAAAO7v/8AAAAAA7u//wAAAAADu7//AAAAAAO7v/8AAAAAA7u//wAAAAADu7//AAAAAAO
+ 7v/8AAAAAA7u//wAAAAADu7//AAAAAAO7v/8AAAAAA7u//wAAAAADu7//AAAAAAO7v/8AAAAAA7u//wA
+ AAAADu7///////4O7igAAAAgAAAAQAAAAAEAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AABmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzMAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmMzOfOwCfOwCfOwCfOwCfOwCfOwCf
+ OwCfOwCfOwCfOwCfOwCfOwBmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAABmMzOkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQBmMzOxmJgA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmMzOrSgCrSgCr
+ SgCrSgCrSgCrSgCrSgCrSgCrSgCrSgCrSgCrSgBmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmMzOwUQCwUQCwUQCwUQCwUQCwUQCwUQCwUQCwUQCwUQCw
+ UQCwUQBmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AABmMzO3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgBmMzOxmJgAAAAAAAAAAAAAAAAA
+ AAAAAADHu9QAM6EAM6EAM6EAM6EAM6EAM6EAM6EAM6EAAAAAAABmMzO8YAC8YAC8YAC8YAC8YAC8YAC8
+ YAC8YAC8YAC8YAC8YAC8YABmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAADHu9QAM6EANqkANqkANqkANqkA
+ NqkANqkAM6F/mM8AAABmMzPDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQBmMzOxmJgA
+ AAAAAAAAAAAAAAAAAAAAAADHu9QAM6EAOK0AOK0AOK0AOK0AOK0AOK0AM6F/mM8AAABmMzPIcADIcADI
+ cADIcADIcADIcADIcADIcADIcADIcADIcADIcABmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAADHu9QAM6EA
+ O7QAO7QAO7QAO7QAO7QAO7QAM6F/mM8AAABmMzPPeADPeADPeADPeADPeADPeADPeADPeADPeADPeADP
+ eADPeABmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAADHu9QAM6EAPbgAPbgAPbgAPbgAPbgAPbgAM6F/mM8A
+ AABmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzOxmJgAAAAAAAAAAAAAAAAA
+ AAAAAADHu9QAM6EAQL8AQL8AQL8AQL8AQL8AQL8AM6FVfMEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADHu9QAM6EAQsMAQsMAQsMAQsMA
+ QsMAQsMAM6EAW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68A
+ W68AW68AW68AW68AW6+BpM7Hu9QAM6EARcoARcoARcoARcoARcoARcoAM6EAW69IuuVIuuVIuuVIuuVI
+ uuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuUAW698oMzHu9QAM6EA
+ R84AR84AR84AR84AR84AR84AM6EAW69IvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZI
+ vuZIvuZIvuZIvuZIvuZIvuZIvuZIvuYAW698oMzHu9QAM6EAStUAStUAStUAStUAStUAStUAM6EAW69H
+ wehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwegA
+ 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
+ SH08XNsEAFaMAdioj++z4Eh9MkLbBABWjAHYsLfvs+BIzR6pbQIAK8YAbEAAYqPmEACUwBiAFr18nwVH
+ au4obRMAWDEGYP2evs+CI/XpaG0TAFgxBmC9Hr7PgiM1b4y2CQCsGAOwLgGIjfqMAKAExgCss6fvs+BI
+ fT5W2wQAVowBaL6H77PgSH0xTtsEAFaMAWjW3fdZcKS+HK9tAgArxgA07e77LDhS88drmwDAijEAa3fz
+ fRYcqQVV2iYAsGIMQJOuvs+CI/XVBG0TAFgxBmCtLr7PgiP19URtEwBYIQDlgQCgJMYArNnZ91lwpL6Z
+ pG0CACvGAKzRyfdZcKQWTtY2AYAVYwBW7+j7LDhSi6q1TQBgxRiA1QhAbNRiAoASGAPQuIPvs+BILZmi
+ bQIAK8YArNre91lwpL6t0TYBgBVjAFZp5/ssOFLfTdU2AYAVYwBWJgCxUd8TAJTAGIBGbX2fBUfqh2na
+ JgCwYgzASm18nwVHaul0bRMAWDEFAPEjALBCAMoTAYAVAlCeCACsEIDyRAAARI8AAAkjAEDCCACQMAIA
+ JIwAAAkjAEDCCACQMFMAAKRlbv1PNdVF4Jm637at+5rUfY3qvrL4iScADfsZgOX03tj+IOMAAAAASUVO
+ RK5CYIIoAAAAMAAAAGAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/svLi/7Ly2D+y8tb/svL
+ W/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svL
+ W/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svL
+ W/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8uU/svL
+ ev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AGUyMhxlMjIgZTIyIGUyMiBlMjIgZTIyIGUyMiBlMjIgZTIyIGUyMiBlMjIgZTIyIGUyMiBlMjIgZTIy
+ IGUyMiBlMjIgZTIyIGUyMiBlMjIgZTIyIGUyMiBlMjIIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAGYzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz
+ /2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+aOQP/njoA/546AP+eOgD/njoA/546AP+eOgD/njoA
+ /546AP+eOgD/njoA/546AP+eOgD/njoA/546AP+eOgD/njoA/546AP+eOgD/njoA/2YzM/9lMjJAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+ePgP/oj8A/6I/AP+iPwD/oj8A
+ /6I/AP+iPwD/oj8A/6I/AP+iPwD/oj8A/6I/AP+iPwD/oj8A/6I/AP+iPwD/oj8A/6I/AP+iPwD/oj8A
+ /2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+iQgP/pkMA
+ /6ZDAP+mQwD/pkMA/6ZDAP+mQwD/pkMA/6ZDAP+mQwD/pkMA/6ZDAP+mQwD/pkMA/6ZDAP+mQwD/pkMA
+ /6ZDAP+mQwD/pkMA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svL
+ ev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AGYzM/+mRwP/qkkA/6pJAP+qSQD/qkkA/6pJAP+qSQD/qkkA/6pJAP+qSQD/qkkA/6pJAP+qSQD/qkkA
+ /6pJAP+qSQD/qkkA/6pJAP+qSQD/qkkA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAGYzM/+pTAP/rk4A/65OAP+uTgD/rk4A/65OAP+uTgD/rk4A/65OAP+uTgD/rk4A
+ /65OAP+uTgD/rk4A/65OAP+uTgD/rk4A/65OAP+uTgD/rk4A/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+tUQP/slMA/7JTAP+yUwD/slMA/7JTAP+yUwD/slMA
+ /7JTAP+yUwD/slMA/7JTAP+yUwD/slMA/7JTAP+yUwD/slMA/7JTAP+yUwD/slMA/2YzM/9lMjJAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+xVgP/tlkA/7ZZAP+2WQD/tlkA
+ /7ZZAP+2WQD/tlkA/7ZZAP+2WQD/tlkA/7ZZAP+2WQD/tlkA/7ZZAP+2WQD/tlkA/7ZZAP+2WQD/tlkA
+ /2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AM6H/ADOh
+ /wAzof8AM6H/ADOh/wAzof8AM6H/ADOh/wAzof8AM6H/ADOh/wAzof8AMqAsAAAAAGYzM/+0WgP/ul0A
+ /7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A
+ /7pdAP+6XQD/ul0A/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svL
+ egAzof8ANKP/ADSk/wA0pP8ANKT/ADSk/wA0pP8ANKT/ADSk/wA0pP8ANKT/ADSk/wAzof8AMqBAAAAA
+ AGYzM/+4XwP/vmIA/75iAP++YgD/vmIA/75iAP++YgD/vmIA/75iAP++YgD/vmIA/75iAP++YgD/vmIA
+ /75iAP++YgD/vmIA/75iAP++YgD/vmIA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAD+y8tb/svLegAzof8ANqf/ADao/wA2qP8ANqj/ADao/wA2qP8ANqj/ADao/wA2qP8ANqj/ADao
+ /wAzof8AMqBAAAAAAGYzM/+8ZAP/wmgA/8JoAP/CaAD/wmgA/8JoAP/CaAD/wmgA/8JoAP/CaAD/wmgA
+ /8JoAP/CaAD/wmgA/8JoAP/CaAD/wmgA/8JoAP/CaAD/wmgA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AN6v/ADer/wA3q/8AN6v/ADer/wA3q/8AN6v/ADer
+ /wA3q/8AN6v/ADer/wAzof8AMqBAAAAAAGYzM//AaQP/xm0A/8ZtAP/GbQD/xm0A/8ZtAP/GbQD/xm0A
+ /8ZtAP/GbQD/xm0A/8ZtAP/GbQD/xm0A/8ZtAP/GbQD/xm0A/8ZtAP/GbQD/xm0A/2YzM/9lMjJAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AOa7/ADmv/wA5r/8AOa//ADmv
+ /wA5r/8AOa//ADmv/wA5r/8AOa//ADmv/wAzof8AMqBAAAAAAGYzM//DbgP/ynIA/8pyAP/KcgD/ynIA
+ /8pyAP/KcgD/ynIA/8pyAP/KcgD/ynIA/8pyAP/KcgD/ynIA/8pyAP/KcgD/ynIA/8pyAP/KcgD/ynIA
+ /2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AOrH/ADuz
+ /wA7s/8AO7P/ADuz/wA7s/8AO7P/ADuz/wA7s/8AO7P/ADuz/wAzof8AMqBAAAAAAGYzM//HcwP/zncA
+ /853AP/OdwD/zncA/853AP/OdwD/zncA/853AP/OdwD/zncA/853AP/OdwD/zncA/853AP/OdwD/zncA
+ /853AP/OdwD/zncA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svL
+ egAzof8APLX/ADy2/wA8tv8APLb/ADy2/wA8tv8APLb/ADy2/wA8tv8APLb/ADy2/wAzof8AMqBAAAAA
+ AGYzM//LdwP/0nwA/9J8AP/SfAD/0nwA/9J8AP/SfAD/0nwA/9J8AP/SfAD/0nwA/9J8AP/SfAD/0nwA
+ /9J8AP/SfAD/0nwA/9J8AP/SfAD/0nwA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAD+y8tb/svLegAzof8APbj/AD66/wA+uv8APrr/AD66/wA+uv8APrr/AD66/wA+uv8APrr/AD66
+ /wAzof8AMqBAAAAAAGYzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz
+ /2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AP7z/AEC+/wBAvv8AQL7/AEC+/wBAvv8AQL7/AEC+
+ /wBAvv8AQL7/AEC+/wAzof8AMqBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AQb//AEHB/wBBwf8AQcH/AEHB
+ /wBBwf8AQcH/AEHB/wBBwf8AQcH/AEHB/wAzof8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv
+ /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv
+ /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/+InsiS/svLegAzof8AQsP/AEPF
+ /wBDxf8AQ8X/AEPF/wBDxf8AQ8X/AEPF/wBDxf8AQ8X/AEPF/wAzof8AW6//SLjk/0i45P9IuOT/SLjk
+ /0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk
+ /0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/wBbr/9+mMWk/svL
+ egAzof8ARMb/AEXJ/wBFyf8ARcn/AEXJ/wBFyf8ARcn/AEXJ/wBFyf8ARcn/AEXJ/wAzof8AW6//SLrl
+ /0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl
+ /0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl
+ /wBbr/9+mMWk/svLegAzof8ARcr/AEfM/wBHzP8AR8z/AEfM/wBHzP8AR8z/AEfM/wBHzP8AR8z/AEfM
+ /wAzof8AW6//SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm
+ /0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm
+ /0i85v9IvOb/SLzm/wBbr/9+mMWk/svLegAzof8AR83/AEjQ/wBI0P8ASND/AEjQ/wBI0P8ASND/AEjQ
+ /wBI0P8ASND/AEjQ/wAzof8AW6//SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n
+ /0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n
+ /0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/wBbr/9+mMWk/svLegAzof8ASNH/AErU/wBK1P8AStT/AErU
+ /wBK1P8AStT/AErU/wBK1P8AStT/AErU/wAzof8AW6//R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho
+ /0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho
+ /0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/wBbr/9+mMWk/8zMegAzof8AStT/AEzY
+ /wBM2P8ATNj/AEzY/wBM2P8ATNj/AEzY/wBM2P8ATNj/AEzY/wAzof8AW6//R8Pp/0fD6f9Hw+n/R8Pp
+ /0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp
+ /0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/wBbr/9+mMWk/8zM
+ egAzof8ATNj/AE3b/wBN2/8ATdv/AE3b/wBN2/8ATdv/AE3b/wBN2/8ATdv/AE3b/wAzof8AW6//R8Xq
+ /0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq
+ /0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq
+ /wBbr/9+mMWk/8zMegAzof8ARsz/AEjP/wBIz/8ASM//AEjP/wBIz/8ASM//AEjP/wBIz/8ASM//AEjP
+ /wAzof8AW6//R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr
+ /0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr
+ /0fI6/9HyOv/R8jr/wBbr/9+mMWk/8zMegAzof8AM6H/ADOh/wAzof8AM6H/ADOh/wAzof8AM6H/ADOh
+ /wAzof8AM6H/ADOh/wAzof8AW6//R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs
+ /0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs
+ /0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt
+ /0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt
+ /0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rs7u/0bO7v9Gzu7/Rs7u
+ /0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u
+ /0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/wBbr/9+mMWk/8zM
+ ev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//RtHv
+ /0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv
+ /0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv
+ /wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAW6//RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw
+ /0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw
+ /0bT8P9G0/D/RtPw/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAW6//RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy
+ /0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy
+ /0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz
+ /0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz
+ /0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rtr0/0ba9P9G2vT/Rtr0
+ /0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0
+ /0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/wBbr/9+mMWk/svL
+ ev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rdz1
+ /0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1
+ /0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1
+ /wBbr/9+mMWk/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAW6//Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72
+ /0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72
+ /0Xe9v9F3vb/Rd72/wBbr/9+mMWk/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAW6//ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3
+ /0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3
+ /0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/wBbr/9+mMWk/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4
+ /0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4
+ /0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/wBbr/9+mMWk/svLev7LywgAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//ReX5/0Xl+f9F5fn/ReX5
+ /0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5
+ /0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/wBbr/9+mMWk/svL
+ ev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Ref6
+ /0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6
+ /0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6
+ /wBbr/9+mMWk/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAW6//ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7
+ /0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7
+ /0Tp+/9E6fv/ROn7/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAW6//ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8
+ /0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8
+ /0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/wBbr/9+mMWk/svLev/LywgAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79
+ /0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79
+ /0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/wBbr/9+mMWk/svLev7LywgAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//RPD+/0Tw/v9E8P7/RPD+
+ /0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+
+ /0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/wBbr/+BmMSi/svL
+ ev7Lyw7+y8sI/8zMCP/MzAj+y8sI/svLCP7Lywj+y8sI/svLCP7Lywj+y8sI/8zMCP/MzAgAW6//AFuv
+ /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv
+ /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv
+ /wBbr//NtcaA/svLfP/Ly3r+y8t6/8zMev/MzHr+y8t6/svLev7Ly3r+y8t6/svLev7Ly3r+y8t6/8zM
+ ev/MzHr/zMx6/8zMev/MzHr/zMx6/8zMev/MzHr/zMx6/8zMev/MzHr+y8t6/svLev7Ly3r+y8t6/svL
+ ev7Ly3r+y8t6/svLev7Ly3r+y8t6/svLev7Ly3r+y8t6/svLev7Ly3r+y8t6/svLev7Ly3r+y8t6/svL
+ ev7Ly3r+y8t6/svLev7Ly3r+y8uLAAAAAAAADu4//wAAAf4O7j//AAAB/g7uP/8AAAH+Du4//wAAAf4O
+ 7j//AAAB/g7uP/8AAAH+Du4//wAAAf4O7j//AAAB/g7uP/8AAAH+Du4AAQAAAf4O7gABAAAB/g7uAAEA
+ AAH+Du4AAQAAAf4O7gABAAAB/g7uAAEAAAH+Du4AAQAAAf4O7gABAAAB/g7uAAH////+Du4AAAAAAAAO
+ 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
+ PamYvaYAM6H/AEC//wBAv/8AQL//AEC//wBAv/8AQL//ADOh/wA7o6oAW69VAFuvVQBbr1UAW69VAFuv
+ VQBbr1UAW69VAFuvVQBbr1UAW69VAFuvVQBbr1UAW69VAFuvVQBbr1UAW69VAFuvVQBbr1UAW69VAFuv
+ VQBbr1WsqMRlqZi9pgAzof8AQsP/AELD/wBCw/8AQsP/AELD/wBCw/8AM6H/AFuv/wBbr/8AW6//AFuv
+ /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv
+ /wBbr/8AW6//AFuv/1aFvr6pmL2mADOh/wBFyv8ARcr/AEXK/wBFyv8ARcr/AEXK/wAzof8AW6//SLrl
+ /0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl
+ /0i65f9IuuX/SLrl/0i65f8AW6//VIO9wqmYvaYAM6H/AEfO/wBHzv8AR87/AEfO/wBHzv8AR87/ADOh
+ /wBbr/9Ivub/SL7m/0i+5v9Ivub/SL7m/0i+5v9Ivub/SL7m/0i+5v9Ivub/SL7m/0i+5v9Ivub/SL7m
+ /0i+5v9Ivub/SL7m/0i+5v9Ivub/SL7m/wBbr/9Ug73CqZi9pgAzof8AStX/AErV/wBK1f8AStX/AErV
+ /wBK1f8AM6H/AFuv/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho
+ /0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/AFuv/1SDvcKqmb2mADOh/wBM2v8ATNr/AEza
+ /wBM2v8ATNr/AEza/wAzof8AW6//R8Tp/0fE6f9HxOn/R8Tp/0fE6f9HxOn/R8Tp/0fE6f9HxOn/R8Tp
+ /0fE6f9HxOn/R8Tp/0fE6f9HxOn/R8Tp/0fE6f9HxOn/R8Tp/0fE6f8AW6//VIO9wqqZvaYAM6H/ADOh
+ /wAzof8AM6H/ADOh/wAzof8AM6H/ADOh/wBbr/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr
+ /0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/wBbr/9Ug73C4rvH
+ b15rsFcAM6FVADOhVQAzoVUAM6FVADOhVQAzoVUAM6FVAFuv/0fL7P9Hy+z/R8vs/0fL7P9Hy+z/R8vs
+ /0fL7P9Hy+z/R8vs/0fL7P9Hy+z/R8vs/0fL7P9Hy+z/R8vs/0fL7P9Hy+z/R8vs/0fL7P9Hy+z/AFuv
+ /1SDvcL/zMxU/8zMAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rs/u/0bP7v9Gz+7/Rs/u
+ /0bP7v9Gz+7/Rs/u/0bP7v9Gz+7/Rs/u/0bP7v9Gz+7/Rs/u/0bP7v9Gz+7/Rs/u/0bP7v9Gz+7/Rs/u
+ /0bP7v8AW6//VIO9wv/MzFT/zMwDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr/9G0u//RtLv
+ /0bS7/9G0u//RtLv/0bS7/9G0u//RtLv/0bS7/9G0u//RtLv/0bS7/9G0u//RtLv/0bS7/9G0u//RtLv
+ /0bS7/9G0u//RtLv/wBbr/9Ug73C/8zMVP/MzAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFuv
+ /0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy
+ /0bV8v9G1fL/RtXy/0bV8v9G1fL/AFuv/1SDvcL/zMxU/8zMAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAW6//Rtnz/0bZ8/9G2fP/Rtnz/0bZ8/9G2fP/Rtnz/0bZ8/9G2fP/Rtnz/0bZ8/9G2fP/Rtnz
+ /0bZ8/9G2fP/Rtnz/0bZ8/9G2fP/Rtnz/0bZ8/8AW6//VIO9wv7Ly1T+y8sDAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAABbr/9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1
+ /0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/wBbr/9Ug73C/svLVP7LywMAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAFuv/0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/Rd/2
+ /0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/AFuv/1SDvcL+y8tU/svL
+ AwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4
+ /0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P8AW6//VIO9
+ wv7Ly1T+y8sDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr/9F5vn/Reb5/0Xm+f9F5vn/Reb5
+ /0Xm+f9F5vn/Reb5/0Xm+f9F5vn/Reb5/0Xm+f9F5vn/Reb5/0Xm+f9F5vn/Reb5/0Xm+f9F5vn/Reb5
+ /wBbr/9Ug73C/svLVP7LywMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFuv/0Tq+/9E6vv/ROr7
+ /0Tq+/9E6vv/ROr7/0Tq+/9E6vv/ROr7/0Tq+/9E6vv/ROr7/0Tq+/9E6vv/ROr7/0Tq+/9E6vv/ROr7
+ /0Tq+/9E6vv/AFuv/1SDvcL+y8tU/8vLAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//RO38
+ /0Tt/P9E7fz/RO38/0Tt/P9E7fz/RO38/0Tt/P9E7fz/RO38/0Tt/P9E7fz/RO38/0Tt/P9E7fz/RO38
+ /0Tt/P9E7fz/RO38/0Tt/P8AW6//VIO9wv7Ly1X+y8sF/8zMA/7LywP+y8sD/svLA/7LywP+y8sD/8zM
+ AwBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv
+ /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/9mir25/svLb/7Ly1X/zMxU/svLVP7Ly1T+y8tU/svL
+ VP7Ly1T/zMxUxrLFi6qmwqaqpsKmqqbCpqqmwqaqpsKmqaXBpqmlwaappcGmqaXBpqmlwaappcGmqaXB
+ pqmlwaappcGmqaXBpqmlwaappcGmqaXBpqmlwaappcGmqaXBpta5xpIAAAAAP8AAPj/AAD4/wAA+P8AA
+ Pj/AAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAAAAAAAAAAAACgAAAAQAAAAIAAA
+ AAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tB/svLHv7Lyx7+y8se/svLHp1qanCYZWWjmGVl
+ o5hlZaOYZWWjmGVlo5hlZaOYZWV4/svLHv7Lyx7+y8s5/svLLAAAAAAAAAAAAAAAAAAAAABmMzP/ZjMz
+ /2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/wAAAAAAAAAA/svLHv7LyywAAAAAAAAAAAAAAAAAAAAAZjMz
+ /61NAP+tTQD/rU0A/61NAP+tTQD/rU0A/2YzM/8AAAAAAAAAAP7Lyx4AM6H/ADOh/wAzof8AM6H/ADOh
+ /2YzM/+5XQD/uV0A/7ldAP+5XQD/uV0A/7ldAP9mMzP/AAAAAAAAAAD+y8seADOh/wA3q/8AN6v/ADer
+ /wAzof9mMzP/xWwA/8VsAP/FbAD/xWwA/8VsAP/FbAD/ZjMz/wAAAAAAAAAA/svLHgAzof8APLb/ADy2
+ /wA8tv8AM6H/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/8AAAAAAAAAAP7Lyx4AM6H/AEHB
+ /wBBwf8AQcH/ADOh/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//ADOh
+ /wBGzP8ARsz/AEbM/wAzof9IvOX/SLzl/0i85f9IvOX/SLzl/0i85f9IvOX/SLzl/0i85f9IvOX/AFuv
+ /wAzof8AS9f/AEvX/wBL1/8AM6H/R8Lo/0fC6P9Hwuj/R8Lo/0fC6P9Hwuj/R8Lo/0fC6P9Hwuj/R8Lo
+ /wBbr/8AM6H/ADOh/wAzof8AM6H/ADOh/0fJ6/9Hyev/R8nr/0fJ6/9Hyev/R8nr/0fJ6/9Hyev/R8nr
+ /0fJ6/8AW6///8zMLAAAAAAAAAAAAAAAAABbr/9G0O7/RtDu/0bQ7v9G0O7/RtDu/0bQ7v9G0O7/RtDu
+ /0bQ7v9G0O7/AFuv///MzCwAAAAAAAAAAAAAAAAAW6//Rtfy/0bX8v9G1/L/Rtfy/0bX8v9G1/L/Rtfy
+ /0bX8v9G1/L/Rtfy/wBbr//+y8ssAAAAAAAAAAAAAAAAAFuv/0Xd9f9F3fX/Rd31/0Xd9f9F3fX/Rd31
+ /0Xd9f9F3fX/Rd31/0Xd9f8AW6///svLLAAAAAAAAAAAAAAAAABbr/9F5Pj/ReT4/0Xk+P9F5Pj/ReT4
+ /0Xk+P9F5Pj/ReT4/0Xk+P9F5Pj/AFuv//7LyywAAAAAAAAAAAAAAAAAW6//ROv7/0Tr+/9E6/v/ROv7
+ /0Tr+/9E6/v/ROv7/0Tr+/9E6/v/ROv7/wBbr//+y8tI/svLLP7Lyyz+y8ssAFuv/wBbr/8AW6//AFuv
+ /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AACsQXgGrEF4BqxBAAasQQAGrEEABqxBAACs
+ QQAArEEAAKxBAACsQXAArEFwAKxBcACsQXAArEFwAKxBAACsQQ==
+
+
+
\ No newline at end of file
diff --git a/branches/ph-plugins/ExtraTools/ProcessAnalyzer/ProcessAnalyzer.csproj b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/ProcessAnalyzer.csproj
new file mode 100644
index 000000000..eeba5aae8
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/ProcessAnalyzer.csproj
@@ -0,0 +1,98 @@
+
+
+
+ Debug
+ AnyCPU
+ 9.0.30729
+ 2.0
+ {A6709B97-F7B5-40AD-AB6E-F23019BA0A3C}
+ WinExe
+ Properties
+ ProcessAnalyzer
+ ProcessAnalyzer
+ v2.0
+ 512
+ fake_base.ico
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ true
+ AnyCPU
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ true
+ AnyCPU
+
+
+
+
+
+
+
+
+
+
+
+ Form
+
+
+ MainWindow.cs
+
+
+
+
+ MainWindow.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+ Designer
+
+
+ True
+ Resources.resx
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+ True
+ Settings.settings
+ True
+
+
+
+
+ {8E10F5E8-D4FA-4980-BB23-2EDD134AC15E}
+ ProcessHacker.Common
+
+
+ {8A448157-E1A7-4DDF-954E-287F1117832B}
+ ProcessHacker.Native
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/branches/ph-plugins/ExtraTools/ProcessAnalyzer/Program.cs b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/Program.cs
new file mode 100644
index 000000000..72127b455
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/Program.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Windows.Forms;
+
+namespace ProcessAnalyzer
+{
+ static class Program
+ {
+ ///
+ /// The main entry point for the application.
+ ///
+ [STAThread]
+ static void Main()
+ {
+ Application.EnableVisualStyles();
+ Application.SetCompatibleTextRenderingDefault(false);
+ Application.Run(new MainWindow());
+ }
+ }
+}
diff --git a/branches/ph-plugins/ExtraTools/ProcessAnalyzer/Properties/AssemblyInfo.cs b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..ca8c088ab
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Process Analyzer")]
+[assembly: AssemblyDescription("Process Analyzer")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("wj32")]
+[assembly: AssemblyProduct("Process Analyzer")]
+[assembly: AssemblyCopyright("Copyright © 2009 wj32. Licensed under the GNU GPL, v3.")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("c98807ff-33c2-465b-a70c-8b0ac257ff96")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/branches/ph-plugins/ExtraTools/ProcessAnalyzer/Properties/Resources.Designer.cs b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/Properties/Resources.Designer.cs
new file mode 100644
index 000000000..dacacce16
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/Properties/Resources.Designer.cs
@@ -0,0 +1,71 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:2.0.50727.4016
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace ProcessAnalyzer.Properties
+{
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources
+ {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources()
+ {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager
+ {
+ get
+ {
+ if ((resourceMan == null))
+ {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ProcessAnalyzer.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture
+ {
+ get
+ {
+ return resourceCulture;
+ }
+ set
+ {
+ resourceCulture = value;
+ }
+ }
+ }
+}
diff --git a/branches/ph-plugins/ExtraTools/ProcessAnalyzer/Properties/Resources.resx b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/Properties/Resources.resx
new file mode 100644
index 000000000..ffecec851
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/Properties/Resources.resx
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/branches/ph-plugins/ExtraTools/ProcessAnalyzer/Properties/Settings.Designer.cs b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/Properties/Settings.Designer.cs
new file mode 100644
index 000000000..8a1647c9e
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/Properties/Settings.Designer.cs
@@ -0,0 +1,30 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:2.0.50727.4016
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace ProcessAnalyzer.Properties
+{
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "9.0.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
+ {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default
+ {
+ get
+ {
+ return defaultInstance;
+ }
+ }
+ }
+}
diff --git a/branches/ph-plugins/ExtraTools/ProcessAnalyzer/Properties/Settings.settings b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/Properties/Settings.settings
new file mode 100644
index 000000000..abf36c5d3
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/Properties/Settings.settings
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/branches/ph-plugins/ExtraTools/ProcessAnalyzer/fake_base.ico b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/fake_base.ico
new file mode 100644
index 000000000..e724a6b20
Binary files /dev/null and b/branches/ph-plugins/ExtraTools/ProcessAnalyzer/fake_base.ico differ
diff --git a/branches/ph-plugins/ExtraTools/SysCallHacker/EventProperties.Designer.cs b/branches/ph-plugins/ExtraTools/SysCallHacker/EventProperties.Designer.cs
new file mode 100644
index 000000000..ae5fa185b
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/SysCallHacker/EventProperties.Designer.cs
@@ -0,0 +1,312 @@
+namespace SysCallHacker
+{
+ partial class EventProperties
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.tabControl = new System.Windows.Forms.TabControl();
+ this.tabEvent = new System.Windows.Forms.TabPage();
+ this.groupArguments = new System.Windows.Forms.GroupBox();
+ this.listArguments = new System.Windows.Forms.ListView();
+ this.columnIndex = new System.Windows.Forms.ColumnHeader();
+ this.columnValue = new System.Windows.Forms.ColumnHeader();
+ this.columnExtendedValue = new System.Windows.Forms.ColumnHeader();
+ this.columnType = new System.Windows.Forms.ColumnHeader();
+ this.groupBasic = new System.Windows.Forms.GroupBox();
+ this.textSystemCall = new System.Windows.Forms.TextBox();
+ this.textMode = new System.Windows.Forms.TextBox();
+ this.textTime = new System.Windows.Forms.TextBox();
+ this.label1 = new System.Windows.Forms.Label();
+ this.labelMode = new System.Windows.Forms.Label();
+ this.tabProcess = new System.Windows.Forms.TabPage();
+ this.tabStackTrace = new System.Windows.Forms.TabPage();
+ this.listStackTrace = new System.Windows.Forms.ListView();
+ this.columnAddress = new System.Windows.Forms.ColumnHeader();
+ this.columnSymbol = new System.Windows.Forms.ColumnHeader();
+ this.buttonClose = new System.Windows.Forms.Button();
+ this.tabControl.SuspendLayout();
+ this.tabEvent.SuspendLayout();
+ this.groupArguments.SuspendLayout();
+ this.groupBasic.SuspendLayout();
+ this.tabStackTrace.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // tabControl
+ //
+ this.tabControl.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.tabControl.Controls.Add(this.tabEvent);
+ this.tabControl.Controls.Add(this.tabProcess);
+ this.tabControl.Controls.Add(this.tabStackTrace);
+ this.tabControl.Location = new System.Drawing.Point(12, 12);
+ this.tabControl.Name = "tabControl";
+ this.tabControl.SelectedIndex = 0;
+ this.tabControl.Size = new System.Drawing.Size(510, 409);
+ this.tabControl.TabIndex = 0;
+ //
+ // tabEvent
+ //
+ this.tabEvent.Controls.Add(this.groupArguments);
+ this.tabEvent.Controls.Add(this.groupBasic);
+ this.tabEvent.Location = new System.Drawing.Point(4, 22);
+ this.tabEvent.Name = "tabEvent";
+ this.tabEvent.Padding = new System.Windows.Forms.Padding(3);
+ this.tabEvent.Size = new System.Drawing.Size(502, 383);
+ this.tabEvent.TabIndex = 0;
+ this.tabEvent.Text = "Event";
+ this.tabEvent.UseVisualStyleBackColor = true;
+ //
+ // groupArguments
+ //
+ this.groupArguments.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.groupArguments.Controls.Add(this.listArguments);
+ this.groupArguments.Location = new System.Drawing.Point(6, 138);
+ this.groupArguments.Name = "groupArguments";
+ this.groupArguments.Size = new System.Drawing.Size(490, 239);
+ this.groupArguments.TabIndex = 2;
+ this.groupArguments.TabStop = false;
+ this.groupArguments.Text = "Arguments";
+ //
+ // listArguments
+ //
+ this.listArguments.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
+ this.columnIndex,
+ this.columnValue,
+ this.columnExtendedValue,
+ this.columnType});
+ this.listArguments.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.listArguments.FullRowSelect = true;
+ this.listArguments.HideSelection = false;
+ this.listArguments.Location = new System.Drawing.Point(3, 16);
+ this.listArguments.Name = "listArguments";
+ this.listArguments.ShowItemToolTips = true;
+ this.listArguments.Size = new System.Drawing.Size(484, 220);
+ this.listArguments.TabIndex = 1;
+ this.listArguments.UseCompatibleStateImageBehavior = false;
+ this.listArguments.View = System.Windows.Forms.View.Details;
+ //
+ // columnIndex
+ //
+ this.columnIndex.Text = "Index";
+ this.columnIndex.Width = 40;
+ //
+ // columnValue
+ //
+ this.columnValue.Text = "Value";
+ this.columnValue.Width = 100;
+ //
+ // columnExtendedValue
+ //
+ this.columnExtendedValue.Text = "Extended Value";
+ this.columnExtendedValue.Width = 260;
+ //
+ // columnType
+ //
+ this.columnType.Text = "Type";
+ this.columnType.Width = 80;
+ //
+ // groupBasic
+ //
+ this.groupBasic.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.groupBasic.Controls.Add(this.textSystemCall);
+ this.groupBasic.Controls.Add(this.textMode);
+ this.groupBasic.Controls.Add(this.textTime);
+ this.groupBasic.Controls.Add(this.label1);
+ this.groupBasic.Controls.Add(this.labelMode);
+ this.groupBasic.Location = new System.Drawing.Point(6, 6);
+ this.groupBasic.Name = "groupBasic";
+ this.groupBasic.Size = new System.Drawing.Size(490, 126);
+ this.groupBasic.TabIndex = 1;
+ this.groupBasic.TabStop = false;
+ this.groupBasic.Text = "Basic";
+ //
+ // textSystemCall
+ //
+ this.textSystemCall.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.textSystemCall.BackColor = System.Drawing.SystemColors.Window;
+ this.textSystemCall.BorderStyle = System.Windows.Forms.BorderStyle.None;
+ this.textSystemCall.Location = new System.Drawing.Point(6, 19);
+ this.textSystemCall.Name = "textSystemCall";
+ this.textSystemCall.ReadOnly = true;
+ this.textSystemCall.Size = new System.Drawing.Size(478, 13);
+ this.textSystemCall.TabIndex = 4;
+ //
+ // textMode
+ //
+ this.textMode.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.textMode.BackColor = System.Drawing.SystemColors.Window;
+ this.textMode.BorderStyle = System.Windows.Forms.BorderStyle.None;
+ this.textMode.Location = new System.Drawing.Point(49, 57);
+ this.textMode.Name = "textMode";
+ this.textMode.ReadOnly = true;
+ this.textMode.Size = new System.Drawing.Size(435, 13);
+ this.textMode.TabIndex = 3;
+ //
+ // textTime
+ //
+ this.textTime.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.textTime.BackColor = System.Drawing.SystemColors.Window;
+ this.textTime.BorderStyle = System.Windows.Forms.BorderStyle.None;
+ this.textTime.Location = new System.Drawing.Point(45, 38);
+ this.textTime.Name = "textTime";
+ this.textTime.ReadOnly = true;
+ this.textTime.Size = new System.Drawing.Size(439, 13);
+ this.textTime.TabIndex = 3;
+ //
+ // label1
+ //
+ this.label1.AutoSize = true;
+ this.label1.Location = new System.Drawing.Point(6, 38);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(33, 13);
+ this.label1.TabIndex = 2;
+ this.label1.Text = "Time:";
+ //
+ // labelMode
+ //
+ this.labelMode.AutoSize = true;
+ this.labelMode.Location = new System.Drawing.Point(6, 57);
+ this.labelMode.Name = "labelMode";
+ this.labelMode.Size = new System.Drawing.Size(37, 13);
+ this.labelMode.TabIndex = 1;
+ this.labelMode.Text = "Mode:";
+ //
+ // tabProcess
+ //
+ this.tabProcess.Location = new System.Drawing.Point(4, 22);
+ this.tabProcess.Name = "tabProcess";
+ this.tabProcess.Padding = new System.Windows.Forms.Padding(3);
+ this.tabProcess.Size = new System.Drawing.Size(502, 383);
+ this.tabProcess.TabIndex = 1;
+ this.tabProcess.Text = "Process";
+ this.tabProcess.UseVisualStyleBackColor = true;
+ //
+ // tabStackTrace
+ //
+ this.tabStackTrace.Controls.Add(this.listStackTrace);
+ this.tabStackTrace.Location = new System.Drawing.Point(4, 22);
+ this.tabStackTrace.Name = "tabStackTrace";
+ this.tabStackTrace.Padding = new System.Windows.Forms.Padding(3);
+ this.tabStackTrace.Size = new System.Drawing.Size(502, 383);
+ this.tabStackTrace.TabIndex = 2;
+ this.tabStackTrace.Text = "Stack Trace";
+ this.tabStackTrace.UseVisualStyleBackColor = true;
+ //
+ // listStackTrace
+ //
+ this.listStackTrace.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
+ this.columnAddress,
+ this.columnSymbol});
+ this.listStackTrace.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.listStackTrace.FullRowSelect = true;
+ this.listStackTrace.HideSelection = false;
+ this.listStackTrace.Location = new System.Drawing.Point(3, 3);
+ this.listStackTrace.Name = "listStackTrace";
+ this.listStackTrace.ShowItemToolTips = true;
+ this.listStackTrace.Size = new System.Drawing.Size(496, 377);
+ this.listStackTrace.TabIndex = 0;
+ this.listStackTrace.UseCompatibleStateImageBehavior = false;
+ this.listStackTrace.View = System.Windows.Forms.View.Details;
+ //
+ // columnAddress
+ //
+ this.columnAddress.Text = "Address";
+ this.columnAddress.Width = 100;
+ //
+ // columnSymbol
+ //
+ this.columnSymbol.Text = "Symbol";
+ this.columnSymbol.Width = 360;
+ //
+ // buttonClose
+ //
+ this.buttonClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+ this.buttonClose.FlatStyle = System.Windows.Forms.FlatStyle.System;
+ this.buttonClose.Location = new System.Drawing.Point(447, 427);
+ this.buttonClose.Name = "buttonClose";
+ this.buttonClose.Size = new System.Drawing.Size(75, 23);
+ this.buttonClose.TabIndex = 1;
+ this.buttonClose.Text = "Close";
+ this.buttonClose.UseVisualStyleBackColor = true;
+ this.buttonClose.Click += new System.EventHandler(this.buttonClose_Click);
+ //
+ // EventProperties
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(534, 462);
+ this.Controls.Add(this.buttonClose);
+ this.Controls.Add(this.tabControl);
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.Name = "EventProperties";
+ this.ShowIcon = false;
+ this.ShowInTaskbar = false;
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+ this.Text = "Event Properties";
+ this.tabControl.ResumeLayout(false);
+ this.tabEvent.ResumeLayout(false);
+ this.groupArguments.ResumeLayout(false);
+ this.groupBasic.ResumeLayout(false);
+ this.groupBasic.PerformLayout();
+ this.tabStackTrace.ResumeLayout(false);
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.TabControl tabControl;
+ private System.Windows.Forms.TabPage tabEvent;
+ private System.Windows.Forms.TabPage tabProcess;
+ private System.Windows.Forms.Button buttonClose;
+ private System.Windows.Forms.TabPage tabStackTrace;
+ private System.Windows.Forms.ListView listStackTrace;
+ private System.Windows.Forms.ColumnHeader columnAddress;
+ private System.Windows.Forms.ColumnHeader columnSymbol;
+ private System.Windows.Forms.GroupBox groupArguments;
+ private System.Windows.Forms.GroupBox groupBasic;
+ private System.Windows.Forms.Label labelMode;
+ private System.Windows.Forms.TextBox textMode;
+ private System.Windows.Forms.TextBox textTime;
+ private System.Windows.Forms.Label label1;
+ private System.Windows.Forms.TextBox textSystemCall;
+ private System.Windows.Forms.ListView listArguments;
+ private System.Windows.Forms.ColumnHeader columnIndex;
+ private System.Windows.Forms.ColumnHeader columnValue;
+ private System.Windows.Forms.ColumnHeader columnExtendedValue;
+ private System.Windows.Forms.ColumnHeader columnType;
+ }
+}
\ No newline at end of file
diff --git a/branches/ph-plugins/ExtraTools/SysCallHacker/EventProperties.cs b/branches/ph-plugins/ExtraTools/SysCallHacker/EventProperties.cs
new file mode 100644
index 000000000..375d40285
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/SysCallHacker/EventProperties.cs
@@ -0,0 +1,154 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Text;
+using System.Windows.Forms;
+using ProcessHacker.Native;
+using ProcessHacker.Native.Api;
+using ProcessHacker.Native.SsLogging;
+using ProcessHacker.Native.Symbols;
+using ProcessHacker.Native.Objects;
+using ProcessHacker.Native.Security;
+using ProcessHacker.Common;
+
+namespace SysCallHacker
+{
+ public partial class EventProperties : Form
+ {
+ private LogEvent _event;
+ private SymbolProvider _symbols;
+
+ public EventProperties(LogEvent even)
+ {
+ InitializeComponent();
+
+ _event = even;
+
+ textSystemCall.Text = MainWindow.SysCallNames.ContainsKey(even.Event.CallNumber) ? MainWindow.SysCallNames[even.Event.CallNumber] : "(unknown)";
+ textTime.Text = _event.Event.Time.ToString();
+ textMode.Text = _event.Event.Mode == KProcessorMode.UserMode ? "User-mode" : "Kernel-mode";
+
+ for (int i = 0; i < _event.Event.Arguments.Length; i++)
+ {
+ ListViewItem item = new ListViewItem();
+
+ item.Text = i.ToString();
+ item.SubItems.Add(new ListViewItem.ListViewSubItem(item, "0x" + _event.Event.Arguments[i].ToString("x")));
+
+ if (_event.Arguments[i] != null)
+ {
+ string text = "";
+ SsData data = _event.Arguments[i];
+
+ if (data is SsSimple)
+ {
+ text = (data as SsSimple).Argument.ToString();
+ }
+ else if (data is SsHandle)
+ {
+ SsHandle handle = data as SsHandle;
+
+ if (!string.IsNullOrEmpty(handle.Name))
+ text = handle.TypeName + ": " + handle.Name;
+ else
+ text = handle.TypeName + ": PID: " + handle.ProcessId.ToString() +
+ ", TID: " + handle.ThreadId.ToString();
+ }
+ else if (data is SsUnicodeString)
+ {
+ text = (data as SsUnicodeString).String;
+ }
+ else if (data is SsObjectAttributes)
+ {
+ SsObjectAttributes oa = data as SsObjectAttributes;
+ text = "";
+
+ if (oa.RootDirectory != null)
+ text = oa.RootDirectory.Name;
+
+ if (oa.ObjectName != null)
+ {
+ if (!string.IsNullOrEmpty(text))
+ text = text + "\\" + oa.ObjectName.String;
+ else
+ text = oa.ObjectName.String;
+ }
+ }
+ else if (data is SsClientId)
+ {
+ text = "PID: " + (data as SsClientId).Original.ProcessId.ToString() +
+ ", TID: " + (data as SsClientId).Original.ThreadId.ToString();
+ }
+
+ item.SubItems.Add(new ListViewItem.ListViewSubItem(item, text));
+ item.SubItems.Add(new ListViewItem.ListViewSubItem(item, _event.Arguments[i].GetType().Name.Remove(0, 2)));
+ }
+ else
+ {
+ item.SubItems.Add(new ListViewItem.ListViewSubItem(item, ""));
+ item.SubItems.Add(new ListViewItem.ListViewSubItem(item, ""));
+ }
+
+ listArguments.Items.Add(item);
+ }
+
+ SymbolProvider.Options = SymbolOptions.DeferredLoads | SymbolOptions.UndName;
+
+ try
+ {
+ using (var phandle = new ProcessHandle(_event.Event.ProcessId,
+ ProcessAccess.QueryInformation | ProcessAccess.VmRead))
+ {
+ _symbols = new SymbolProvider(phandle);
+
+ phandle.EnumModules((module) =>
+ {
+ _symbols.LoadModule(module.FileName, module.BaseAddress, module.Size);
+ return true;
+ });
+ Windows.EnumKernelModules((module) =>
+ {
+ _symbols.LoadModule(module.FileName, module.BaseAddress);
+ return true;
+ });
+ _symbols.PreloadModules = true;
+
+ for (int i = 0; i < _event.Event.StackTrace.Length; i++)
+ {
+ var address = _event.Event.StackTrace[i];
+ string fileName;
+ IntPtr baseAddress;
+
+ fileName = _symbols.GetModuleFromAddress(address, out baseAddress);
+
+ listStackTrace.Items.Add(new ListViewItem(new string[]
+ {
+ "0x" + address.ToString("x"),
+ (new System.IO.FileInfo(fileName)).Name + "+0x" + address.Decrement(baseAddress).ToString("x")
+ }));
+
+ WorkQueue.GlobalQueueWorkItemTag(new Action((i_, address_) =>
+ {
+ string symbol = _symbols.GetSymbolFromAddress(address_.ToUInt64());
+
+ if (this.IsHandleCreated)
+ this.BeginInvoke(new Action(() => listStackTrace.Items[i_].SubItems[1].Text = symbol));
+ }), "resolve-symbol", i, address);
+ }
+ }
+ }
+ catch
+ { }
+
+ listArguments.SetDoubleBuffered(true);
+ listStackTrace.SetDoubleBuffered(true);
+ }
+
+ private void buttonClose_Click(object sender, EventArgs e)
+ {
+ this.Close();
+ }
+ }
+}
diff --git a/branches/ph-plugins/ExtraTools/SysCallHacker/EventProperties.resx b/branches/ph-plugins/ExtraTools/SysCallHacker/EventProperties.resx
new file mode 100644
index 000000000..ff31a6db5
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/SysCallHacker/EventProperties.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/branches/ph-plugins/ExtraTools/SysCallHacker/LogEvent.cs b/branches/ph-plugins/ExtraTools/SysCallHacker/LogEvent.cs
new file mode 100644
index 000000000..0eba597f4
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/SysCallHacker/LogEvent.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using ProcessHacker.Native.SsLogging;
+
+namespace SysCallHacker
+{
+ public class LogEvent
+ {
+ private SsEvent _event;
+ private SsData[] _arguments;
+
+ public LogEvent(SsEvent even)
+ {
+ _event = even;
+ _arguments = new SsData[even.Arguments.Length];
+ }
+
+ public SsData[] Arguments
+ {
+ get { return _arguments; }
+ }
+
+ public SsEvent Event
+ {
+ get { return _event; }
+ }
+ }
+}
diff --git a/branches/ph-plugins/ExtraTools/SysCallHacker/MainWindow.Designer.cs b/branches/ph-plugins/ExtraTools/SysCallHacker/MainWindow.Designer.cs
new file mode 100644
index 000000000..ea3961ba7
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/SysCallHacker/MainWindow.Designer.cs
@@ -0,0 +1,241 @@
+namespace SysCallHacker
+{
+ partial class MainWindow
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.components = new System.ComponentModel.Container();
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainWindow));
+ this.mainMenu = new System.Windows.Forms.MainMenu(this.components);
+ this.hackerMenuItem = new System.Windows.Forms.MenuItem();
+ this.clearHackerMenuItem = new System.Windows.Forms.MenuItem();
+ this.exitMenuItem = new System.Windows.Forms.MenuItem();
+ this.menuItem1 = new System.Windows.Forms.MenuItem();
+ this.removeAllFiltersMenuItem = new System.Windows.Forms.MenuItem();
+ this.addProcessFiltersMenuItem = new System.Windows.Forms.MenuItem();
+ this.listEvents = new System.Windows.Forms.ListView();
+ this.columnTime = new System.Windows.Forms.ColumnHeader();
+ this.columnClient = new System.Windows.Forms.ColumnHeader();
+ this.columnCall = new System.Windows.Forms.ColumnHeader();
+ this.columnMode = new System.Windows.Forms.ColumnHeader();
+ this.columnArguments = new System.Windows.Forms.ColumnHeader();
+ this.imageList = new System.Windows.Forms.ImageList(this.components);
+ this.toolBarButtonStop = new System.Windows.Forms.ToolBarButton();
+ this.toolBarButtonStart = new System.Windows.Forms.ToolBarButton();
+ this.toolBar = new System.Windows.Forms.ToolBar();
+ this.statusBar = new System.Windows.Forms.StatusBar();
+ this.timerUpdate = new System.Windows.Forms.Timer(this.components);
+ this.SuspendLayout();
+ //
+ // mainMenu
+ //
+ this.mainMenu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
+ this.hackerMenuItem,
+ this.menuItem1});
+ //
+ // hackerMenuItem
+ //
+ this.hackerMenuItem.Index = 0;
+ this.hackerMenuItem.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
+ this.clearHackerMenuItem,
+ this.exitMenuItem});
+ this.hackerMenuItem.Text = "Hacker";
+ //
+ // clearHackerMenuItem
+ //
+ this.clearHackerMenuItem.Index = 0;
+ this.clearHackerMenuItem.Text = "Clear";
+ this.clearHackerMenuItem.Click += new System.EventHandler(this.clearHackerMenuItem_Click);
+ //
+ // exitMenuItem
+ //
+ this.exitMenuItem.Index = 1;
+ this.exitMenuItem.Text = "E&xit";
+ this.exitMenuItem.Click += new System.EventHandler(this.exitMenuItem_Click);
+ //
+ // menuItem1
+ //
+ this.menuItem1.Index = 1;
+ this.menuItem1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
+ this.removeAllFiltersMenuItem,
+ this.addProcessFiltersMenuItem});
+ this.menuItem1.Text = "Filters";
+ //
+ // removeAllFiltersMenuItem
+ //
+ this.removeAllFiltersMenuItem.Index = 0;
+ this.removeAllFiltersMenuItem.Text = "Remove All";
+ this.removeAllFiltersMenuItem.Click += new System.EventHandler(this.removeAllFiltersMenuItem_Click);
+ //
+ // addProcessFiltersMenuItem
+ //
+ this.addProcessFiltersMenuItem.Index = 1;
+ this.addProcessFiltersMenuItem.Text = "Add Process...";
+ this.addProcessFiltersMenuItem.Click += new System.EventHandler(this.addProcessFiltersMenuItem_Click);
+ //
+ // listEvents
+ //
+ this.listEvents.AllowColumnReorder = true;
+ this.listEvents.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.listEvents.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
+ this.columnTime,
+ this.columnClient,
+ this.columnCall,
+ this.columnMode,
+ this.columnArguments});
+ this.listEvents.FullRowSelect = true;
+ this.listEvents.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable;
+ this.listEvents.HideSelection = false;
+ this.listEvents.Location = new System.Drawing.Point(0, 28);
+ this.listEvents.Name = "listEvents";
+ this.listEvents.ShowItemToolTips = true;
+ this.listEvents.Size = new System.Drawing.Size(813, 435);
+ this.listEvents.TabIndex = 1;
+ this.listEvents.UseCompatibleStateImageBehavior = false;
+ this.listEvents.View = System.Windows.Forms.View.Details;
+ this.listEvents.VirtualMode = true;
+ this.listEvents.DoubleClick += new System.EventHandler(this.listEvents_DoubleClick);
+ this.listEvents.RetrieveVirtualItem += new System.Windows.Forms.RetrieveVirtualItemEventHandler(this.listEvents_RetrieveVirtualItem);
+ //
+ // columnTime
+ //
+ this.columnTime.Text = "Time";
+ this.columnTime.Width = 140;
+ //
+ // columnClient
+ //
+ this.columnClient.Text = "Client";
+ this.columnClient.Width = 120;
+ //
+ // columnCall
+ //
+ this.columnCall.Text = "Call";
+ this.columnCall.Width = 160;
+ //
+ // columnMode
+ //
+ this.columnMode.Text = "Mode";
+ this.columnMode.Width = 70;
+ //
+ // columnArguments
+ //
+ this.columnArguments.Text = "Arguments";
+ this.columnArguments.Width = 300;
+ //
+ // imageList
+ //
+ this.imageList.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("imageList.ImageStream")));
+ this.imageList.TransparentColor = System.Drawing.Color.Transparent;
+ this.imageList.Images.SetKeyName(0, "control_play");
+ this.imageList.Images.SetKeyName(1, "control_stop");
+ //
+ // toolBarButtonStop
+ //
+ this.toolBarButtonStop.ImageKey = "control_stop";
+ this.toolBarButtonStop.Name = "toolBarButtonStop";
+ this.toolBarButtonStop.ToolTipText = "Stop";
+ //
+ // toolBarButtonStart
+ //
+ this.toolBarButtonStart.ImageKey = "control_play";
+ this.toolBarButtonStart.Name = "toolBarButtonStart";
+ this.toolBarButtonStart.ToolTipText = "Start";
+ //
+ // toolBar
+ //
+ this.toolBar.Appearance = System.Windows.Forms.ToolBarAppearance.Flat;
+ this.toolBar.Buttons.AddRange(new System.Windows.Forms.ToolBarButton[] {
+ this.toolBarButtonStart,
+ this.toolBarButtonStop});
+ this.toolBar.DropDownArrows = true;
+ this.toolBar.ImageList = this.imageList;
+ this.toolBar.Location = new System.Drawing.Point(0, 0);
+ this.toolBar.Name = "toolBar";
+ this.toolBar.ShowToolTips = true;
+ this.toolBar.Size = new System.Drawing.Size(813, 28);
+ this.toolBar.TabIndex = 0;
+ this.toolBar.ButtonClick += new System.Windows.Forms.ToolBarButtonClickEventHandler(this.toolBar_ButtonClick);
+ //
+ // statusBar
+ //
+ this.statusBar.Location = new System.Drawing.Point(0, 462);
+ this.statusBar.Name = "statusBar";
+ this.statusBar.Size = new System.Drawing.Size(813, 22);
+ this.statusBar.TabIndex = 2;
+ //
+ // timerUpdate
+ //
+ this.timerUpdate.Enabled = true;
+ this.timerUpdate.Interval = 500;
+ this.timerUpdate.Tick += new System.EventHandler(this.timerUpdate_Tick);
+ //
+ // MainWindow
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(813, 484);
+ this.Controls.Add(this.statusBar);
+ this.Controls.Add(this.listEvents);
+ this.Controls.Add(this.toolBar);
+ this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
+ this.Menu = this.mainMenu;
+ this.Name = "MainWindow";
+ this.Text = "System Call Hacker";
+ this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainWindow_FormClosing);
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.MainMenu mainMenu;
+ private System.Windows.Forms.MenuItem hackerMenuItem;
+ private System.Windows.Forms.MenuItem exitMenuItem;
+ private System.Windows.Forms.ListView listEvents;
+ private System.Windows.Forms.ColumnHeader columnTime;
+ private System.Windows.Forms.ColumnHeader columnCall;
+ private System.Windows.Forms.ColumnHeader columnMode;
+ private System.Windows.Forms.ColumnHeader columnArguments;
+ private System.Windows.Forms.ImageList imageList;
+ private System.Windows.Forms.ToolBarButton toolBarButtonStop;
+ private System.Windows.Forms.ToolBarButton toolBarButtonStart;
+ private System.Windows.Forms.ToolBar toolBar;
+ private System.Windows.Forms.StatusBar statusBar;
+ private System.Windows.Forms.Timer timerUpdate;
+ private System.Windows.Forms.ColumnHeader columnClient;
+ private System.Windows.Forms.MenuItem menuItem1;
+ private System.Windows.Forms.MenuItem removeAllFiltersMenuItem;
+ private System.Windows.Forms.MenuItem addProcessFiltersMenuItem;
+ private System.Windows.Forms.MenuItem clearHackerMenuItem;
+
+
+ }
+}
+
diff --git a/branches/ph-plugins/ExtraTools/SysCallHacker/MainWindow.cs b/branches/ph-plugins/ExtraTools/SysCallHacker/MainWindow.cs
new file mode 100644
index 000000000..81f04cd7a
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/SysCallHacker/MainWindow.cs
@@ -0,0 +1,233 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Windows.Forms;
+using ProcessHacker.Common;
+using ProcessHacker.Native;
+using ProcessHacker.Native.Api;
+using ProcessHacker.Native.Objects;
+using ProcessHacker.Native.Security;
+using ProcessHacker.Native.SsLogging;
+using ProcessHacker.Native.Symbols;
+
+namespace SysCallHacker
+{
+ public partial class MainWindow : Form
+ {
+ private static Dictionary _sysCallNames = new Dictionary();
+
+ public static Dictionary SysCallNames
+ {
+ get { return _sysCallNames; }
+ }
+
+ private SsLogger _logger;
+ private List _events = new List();
+ private LogEvent _lastEvent;
+ private List _rules = new List();
+ private Dictionary _processes;
+
+ public MainWindow()
+ {
+ InitializeComponent();
+
+ Win32.LoadLibrary("C:\\Program Files\\Debugging Tools for Windows (x86)\\dbghelp.dll");
+
+ SymbolProvider symbols = new SymbolProvider(ProcessHandle.Current);
+
+ SymbolProvider.Options |= SymbolOptions.PublicsOnly;
+
+ IntPtr ntdllBase = Loader.GetDllHandle("ntdll.dll");
+ FileHandle ntdllFileHandle = null;
+ Section section = null;
+
+ ProcessHandle.Current.EnumModules((module) =>
+ {
+ if (module.BaseName.Equals("ntdll.dll", StringComparison.InvariantCultureIgnoreCase))
+ {
+ section = new Section(
+ ntdllFileHandle = new FileHandle(@"\??\" + module.FileName,
+ FileShareMode.ReadWrite,
+ FileAccess.GenericExecute | FileAccess.GenericRead
+ ),
+ true,
+ MemoryProtection.ExecuteRead
+ );
+
+ symbols.LoadModule(module.FileName, module.BaseAddress, module.Size);
+ return false;
+ }
+
+ return true;
+ });
+
+ SectionView view = section.MapView((int)ntdllFileHandle.GetSize());
+
+ ntdllFileHandle.Dispose();
+
+ symbols.EnumSymbols("ntdll!Zw*", (symbol) =>
+ {
+ int number = Marshal.ReadInt32(
+ (symbol.Address.ToIntPtr().Decrement(ntdllBase)).Increment(view.Memory).Increment(1));
+
+ _sysCallNames.Add(
+ number,
+ "Nt" + symbol.Name.Substring(2)
+ );
+
+ return true;
+ });
+
+ view.Dispose();
+ section.Dispose();
+
+ symbols.Dispose();
+
+ KProcessHacker.Instance = new KProcessHacker();
+
+ _logger = new SsLogger(4096, false);
+ _logger.EventBlockReceived += new EventBlockReceivedDelegate(logger_EventBlockReceived);
+ _logger.ArgumentBlockReceived += new ArgumentBlockReceivedDelegate(logger_ArgumentBlockReceived);
+ _logger.AddPreviousModeRule(FilterType.Include, KProcessorMode.UserMode);
+ _logger.AddProcessIdRule(FilterType.Exclude, ProcessHandle.GetCurrentId());
+ //_logger.Start();
+
+ listEvents.SetDoubleBuffered(true);
+ }
+
+ private void MainWindow_FormClosing(object sender, FormClosingEventArgs e)
+ {
+ ProcessHandle.Current.Terminate();
+ }
+
+ private void logger_EventBlockReceived(SsEvent eventBlock)
+ {
+ LogEvent logEvent = new LogEvent(eventBlock);
+
+ lock (_events)
+ _events.Add(logEvent);
+
+ _lastEvent = logEvent;
+ }
+
+ private void logger_ArgumentBlockReceived(SsData argBlock)
+ {
+ if (_lastEvent != null)
+ {
+ if (argBlock.Index < _lastEvent.Arguments.Length)
+ _lastEvent.Arguments[argBlock.Index] = argBlock;
+ }
+ }
+
+ private void timerUpdate_Tick(object sender, EventArgs e)
+ {
+ _processes = Windows.GetProcesses();
+
+ lock (_events)
+ listEvents.VirtualListSize = _events.Count;
+
+ int blocksWritten, blocksDropped;
+
+ _logger.GetStatistics(out blocksWritten, out blocksDropped);
+
+ if (blocksWritten > 0 || blocksDropped > 0)
+ {
+ lock (_events)
+ {
+ statusBar.Text = _events.Count.ToString("N0") + " events, " +
+ blocksWritten.ToString("N0") + " blocks, " +
+ blocksDropped.ToString("N0") + " dropped (" +
+ ((double)blocksDropped / (blocksWritten + blocksDropped) * 100).ToString("F2") + "%)";
+ }
+ }
+ }
+
+ private void listEvents_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
+ {
+ LogEvent logEvent;
+ ListViewItem item;
+
+ lock (_events)
+ logEvent = _events[e.ItemIndex];
+
+ string objectName = "";
+
+ if (logEvent.Arguments.Length > 2)
+ {
+ SsObjectAttributes oa = logEvent.Arguments[2] as SsObjectAttributes;
+
+ if (oa != null)
+ {
+ if (oa.ObjectName != null)
+ objectName = oa.ObjectName.String;
+ }
+ }
+
+ item = new ListViewItem(new string[]
+ {
+ logEvent.Event.Time.ToString(),
+ _processes.ContainsKey(logEvent.Event.ProcessId) ? _processes[logEvent.Event.ProcessId].Name : logEvent.Event.ProcessId.ToString(),
+ _sysCallNames.ContainsKey(logEvent.Event.CallNumber) ? _sysCallNames[logEvent.Event.CallNumber] : "(unknown)",
+ logEvent.Event.Mode == KProcessorMode.UserMode ? "User" : "Kernel",
+ objectName
+ });
+ e.Item = item;
+ }
+
+ private void exitMenuItem_Click(object sender, EventArgs e)
+ {
+ this.Close();
+ }
+
+ private void toolBar_ButtonClick(object sender, ToolBarButtonClickEventArgs e)
+ {
+ if (e.Button == toolBarButtonStart)
+ {
+ _logger.Start();
+ }
+ else if (e.Button == toolBarButtonStop)
+ {
+ _logger.Stop();
+ }
+ }
+
+ private void ShowProperties(int index)
+ {
+ (new EventProperties(_events[index])).ShowDialog();
+ }
+
+ private void listEvents_DoubleClick(object sender, EventArgs e)
+ {
+ this.ShowProperties(listEvents.SelectedIndices[0]);
+ }
+
+ private void clearHackerMenuItem_Click(object sender, EventArgs e)
+ {
+ lock (_events)
+ {
+ listEvents.VirtualListSize = 0;
+ _events.Clear();
+ }
+ }
+
+ private void removeAllFiltersMenuItem_Click(object sender, EventArgs e)
+ {
+ foreach (var rule in _rules)
+ _logger.RemoveRule(rule);
+
+ _rules.Clear();
+ }
+
+ private void addProcessFiltersMenuItem_Click(object sender, EventArgs e)
+ {
+ ProcessHacker.Native.Ui.ChooseProcessDialog cpd = new ProcessHacker.Native.Ui.ChooseProcessDialog();
+
+ if (cpd.ShowDialog() == DialogResult.OK)
+ _rules.Add(_logger.AddProcessIdRule(FilterType.Include, cpd.SelectedPid));
+ }
+ }
+}
diff --git a/branches/ph-plugins/ExtraTools/SysCallHacker/MainWindow.resx b/branches/ph-plugins/ExtraTools/SysCallHacker/MainWindow.resx
new file mode 100644
index 000000000..fc7d1066f
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/SysCallHacker/MainWindow.resx
@@ -0,0 +1,953 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 17, 17
+
+
+ 127, 17
+
+
+
+ AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj0yLjAuMC4w
+ LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0
+ ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAAB4
+ BgAAAk1TRnQBSQFMAgEBAgEAAQQBAAEEAQABEAEAARABAAT/ASEBAAj/AUIBTQE2BwABNgMAASgDAAFA
+ AwABEAMAAQEBAAEgBgABEP8AIwADIwE0A0cBgANaAcADYgHtA2IB7QNaAcADRwGAAyMBNCAAAyMBNANH
+ AYADWgHAA2IB7QNiAe0DWgHAA0cBgAMjATScAAM9AWcDXgHVA84B/wPmAf8D9QH/A/IB/wPdAf8DwAH/
+ A14B1QM9AWcYAAM9AWcDXgHVA84B/wPmAf8D9QH/A/IB/wPdAf8DwAH/A14B1QM9AWeUAAM9AWcDbgHx
+ A+wB/wP+Af8D8wH/A+gB/wPnAf8D8QH/A/sB/wPhAf8DZwHxAz0BZxAAAz0BZwNuAfED7AH/A/4B/wPz
+ Af8D6AH/A+cB/wPxAf8D+wH/A+EB/wNnAfEDPQFnjAADIwE0A14B1QPwBf8D5QH/A+UB/wPvAf8D4wH/
+ A+IB/wPhAf8D/gH/A+IB/wNeAdUDIwE0CAADIwE0A14B1QPwBf8D5QH/A+UB/wPkAf8D4wH/A+IB/wPh
+ Af8D/gH/A+IB/wNeAdUDIwE0iAADRwGAA+QB/wP+Af8D6AH/A+cB/wPmAf8DYQH/A+8B/wPkAf8D4wH/
+ A+IB/wP7Af8DxgH/A0cBgAgAA0cBgAPkAf8D/gH/A+gB/wPnAf8D5gH/A+UB/wPlAf8D5AH/A+MB/wPi
+ Af8D+wH/A8YB/wNHAYCIAANaAcAD8gH/A/UB/wPpAf8D6QH/A+gB/wNmAf8BYgJhAf8D7wH/A+UB/wPk
+ Af8D8gH/A+EB/wNaAcAIAANaAcAD8gH/A/UB/wPpAf8D6QH/A4MB/wOCAf8DggH/A4EB/wPlAf8D5AH/
+ A/IB/wPhAf8DWgHAiAADaAHtA/wB/wPvAf8D6wH/A+oB/wPpAf8DYwH/AWcCZgH/AWICYQH/A+8B/wPl
+ Af8D6AH/A/QB/wNkAe0IAANoAe0D/AH/A+8B/wPrAf8D6gH/A1wB/wNcAf8DWwH/A4IB/wPlAf8D5QH/
+ A+gB/wP0Af8DZAHtiAADaAHtA/wB/wPwAf8D7QH/A+sB/wPqAf8DYwH/A2MB/wFoAmcB/wPNAf8D5gH/
+ A+kB/wP6Af8DZAHtCAADaAHtA/wB/wPwAf8D7QH/A+sB/wNcAf8DXAH/A1wB/wODAf8D5wH/A+YB/wPp
+ Af8D+gH/A2QB7YgAA1oBwAP1Af8D9wH/A+0B/wPtAf8D7AH/AWQCYwH/AWQCYwH/A8AB/wPpAf8D6AH/
+ A/QB/wPuAf8DWgHACAADWgHAA/UB/wP3Af8D7QH/A+0B/wNdAf8DXQH/A1wB/wODAf8D6QH/A+gB/wP0
+ Af8D7gH/A1oBwIgAA0cBgAPrBf8D7wH/A+4B/wPtAf8BZQJkAf8DwgH/A+sB/wPpAf8D6QX/A90B/wNH
+ AYAIAANHAYAD6wX/A+8B/wPuAf8D7QH/A+0B/wPsAf8D6wH/A+kB/wPpBf8D3QH/A0cBgIgAAyMBNANe
+ AdUD+AX/A+8B/wPuAf8DwwH/A+0B/wPsAf8D6wX/A/UB/wNeAdUDIwE0CAADIwE0A14B1QP4Bf8D7wH/
+ A+4B/wPtAf8D7QH/A+wB/wPrBf8D9QH/A14B1QMjATSMAAM9AWcDcgHxA/gF/wP4Af8D8gH/A/EB/wP3
+ Bf8D9QH/A3AB8QM9AWcQAAM9AWcDcgHxA/gF/wP4Af8D8gH/A/EB/wP3Bf8D9QH/A3AB8QM9AWeUAAM9
+ AWcDXgHVA+sB/wP1Af8D/AH/A/wB/wP0Af8D6AH/A14B1QM9AWcYAAM9AWcDXgHVA+sB/wP1Af8D/AH/
+ A/wB/wP0Af8D6AH/A14B1QM9AWecAAMjATQDRwGAA1oBwANoAe0DaAHtA1oBwANHAYADIwE0IAADIwE0
+ A0cBgANaAcADaAHtA2gB7QNaAcADRwGAAyMBNP8AkQABQgFNAT4HAAE+AwABKAMAAUADAAEQAwABAQEA
+ AQEFAAGAFwAD/wEABP8EAAHwAQ8B8AEPBAAB4AEHAeABBwQAAcABAwHAAQMEAAGAAQEBgAEBBAABgAEB
+ AYABAQQAAYABAQGAAQEEAAGAAQEBgAEBBAABgAEBAYABAQQAAYABAQGAAQEEAAGAAQEBgAEBBAABgAEB
+ AYABAQQAAcABAwHAAQMEAAHgAQcB4AEHBAAB8AEPAfABDwQABP8EAAs=
+
+
+
+ 230, 17
+
+
+
+
+ AAABAA8AMDAQAAEABABoBgAA9gAAACAgEAABAAQA6AIAAF4HAAAQEBAAAQAEACgBAABGCgAAAAAAAAEA
+ CABqDQAAbgsAADAwAAABAAgAqA4AANgYAAAgIAAAAQAIAKgIAACAJwAAEBAAAAEACABoBQAAKDAAAAAA
+ AAABABgAOQ0AAJA1AAAwMAAAAQAYAKgcAADJQgAAICAAAAEAGACoDAAAcV8AABAQAAABABgAaAMAABls
+ AAAAAAAAAQAgAHANAACBbwAAMDAAAAEAIACoJQAA8XwAACAgAAABACAAqBAAAJmiAAAQEAAAAQAgAGgE
+ AABBswAAKAAAADAAAABgAAAAAQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAIAAAACA
+ gACAAAAAgACAAICAAACAgIAAAAD/AAD/AAAA//8A/wAAAP8A/wD//wAAwMDAAP///wDwAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFRGVGVlZGVkdGVlAAAA
+ AAAAAAAAAAAAAFZGtkZLa2Rka0a0AAAAAAAAAAAAAAAAAGtka2tka2trZkZHAAAAAAAAAAAAAAAAAGRr
+ a2tmtmtra2tkAAAAAAAAAAAAAAAAAFZrZma2a2a2a2tnAAAAAAAAAAAAAAAAAEa2a2tra2tra2ZkAAAA
+ AAAAAAAAAAAAAGa2trZra2a2a2tlAAAAAAAAAAAAAAAAAHa2bbZrZttmtmtmAAAAAAABODE4ExgxAFa2
+ tra2tra2tmtlAAAAAAABgxg4E4OBAEZrZrZr1rZr29tmAAAAAAADgTETgxMTAHvWvb22tmvba2tlAAAA
+ AAADE4ODgxg4AGRr272729tr29vUAAAAAAAIE4MTgTgxAF2729vb22bb29tnAAAAAAABODg4ODgxAEbb
+ 29vb29u2vb22AAAAAAADg4ODg4ODAEZmZmZmZm1mZmZlAAAAAAABODg4ODg4AHR2VlZWR1ZHRlZWAAAA
+ AAAIODg4ODg4AAAAAAAAAAAAAAAAAAAAAAADg4ODg4ODM4ODiDiDg4ODg4OIOIODg44BODiDioOBiuiu
+ p6euinqK6K6np66o6j4BioODg4ODOurqjq6nrq6urqeup3qK6h4Dg4OKg4ODjoruqK6o6o6o6uqOqurq
+ 6o4Bg4ODiDg4Oq6orqeup66np6iuqOqOqD4Dg4qIOKg4h6eup66Kenp6eurqeup66j4Biog4qDiDPqen
+ p66urq6np66K6np6eo4DiKg4OKiBiq6nrqiuqKeup6p6enp66j4Bg4OIODgzOup66nrqeup6eurqenrq
+ eo4BMRMTgTGBh66orqenp6rorop66np66j4AAAAAAAAAOup66np66nrqrqrqenrqeo4AAAAAAAAAinp6
+ eup6enrqeurqeup66j4AAAAAAAAAPqrq6qeq6up6euqK6q6uqh4AAAAAAAAAiup6eup6eqeup66urqiu
+ 6j4AAAAAAAAAOup66np66n6qeqeqenrqqo4AAAAAAAAAGq6q6q6q6qrq6urqrqrq6j4AAAAAAAAAPq6u
+ qurq6urq6uqurq6q6o4AAAAAAAAAiq6q6uqq6q6qququqq6q6j4AAAAAAAAAOuqurqrq6uqurq6urq6u
+ ro4AAAAAAAAAiurqqurq6q6urqrqrqquqj4AAAAAAAAAPq6q6urqququqq6q6q6uro4AAAAAAAAAOqrq
+ 6qqurq6q6urqrq6q6j4AAAAAAAAAOurqqurqrqququqq6uquqj4AAAAAAAAAiuqurqrqrq6q6q6uqq6q
+ 6o4AAAAAAAAAOuququrqrqrq6q6q6uquqn4AAAAAAAAAeq6uququrqrqrq6q6q6uqo4AAAAAAAAAOq6q
+ rqquqq6qrqquqq6qrj4AAAAAAAAAgzODM4MzgzODM4MzgzODMT8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AA9///////4AAP///////wAA//8AAAP/AAD//wAAA/8AAP//AAAD/wAA//8AAAP/AAD//wAAA/8AAP//
+ AAAD/wAA//8AAAP/AAD//wAAA/8AAIADAAAD/wAAgAMAAAP/AACAAwAAA/8AAIADAAAD/wAAgAMAAAP/
+ AACAAwAAA/8AAIADAAAD/wAAgAMAAAP/AACAA/////8AAIAAAAAAAAAAgAAAAAAAAACAAAAAAAAAAIAA
+ AAAAAAAAgAAAAAAAAACAAAAAAAAAAIAAAAAAAAAAgAAAAAAAAACAAAAAAAAAAIAAAAAAAAAA//wAAAAA
+ AAD//AAAAAAAAP/8AAAAAAAA//wAAAAAAAD//AAAAAAAAP/8AAAAAAAA//wAAAAAAAD//AAAAAAAAP/8
+ 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
+ Hx8fHx8fHx8fGwAAARQXFxcXFxcXFxcXFxcXFxcXFxcXAQAAAAAAAAAAAAAAGyAhISEhISEhISEgGwAA
+ ARYZGRkZGRkZGRkZGRkZGRkZGRkZAQAAAAAAAAAAAAAAGyEhISEhISEhISEhGwAAARgaGhoaGhoaGhoa
+ GhoaGhoaGhoaAQAAAAAAAAAAAAAAGyIiIyIjIyIjIyMiGwAAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAA
+ AAAAAAAAAAAAGyUlJSUlJSUlJSUlGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGyYm
+ JiYmJiYmJiYmGyQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJEIAGyYnJygnKCcoJycnHCQv
+ Ly8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vJEEAGygpKSkpKSkpKSkpHCQwLzAwMDAwMDAwMDAw
+ MDAwMDAwMDAwMDAwMDAwMDAvJEEAGykrKiorKisqKyoqHCQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw
+ MDAwMDAwJEEAGyosLCwsLCwsLCwsGyQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwJEEAGywt
+ LS0tLS0tLS0tHCQxMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExJEEAGy0uLi4uLi4uLi4uHCQy
+ MjIxMjEyMTIxMjEyMTIxMjEyMTIxMjEyMTIxMjEyJEEAGy4uLi4uLi4uLi4uGyQyMjIyMjIyMjIyMjIy
+ MjIyMjIyMjIyMjIyMjIyMjIyJEEAGyorKysrKysrKysrGyQzMzQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0
+ NDQ0NDQzJEEAGxsbGxsbGxsbGxsbHCQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDU0JEEAAAAA
+ AAAAAAAAAAAAACQ1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1JEEAAAAAAAAAAAAAAAAAACQ1
+ NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1JEEAAAAAAAAAAAAAAAAAACQ2Njc3Nzc3Nzc3Nzc3
+ Nzc3Nzc3Nzc3Nzc3Nzc3NzY2JEEAAAAAAAAAAAAAAAAAACQ3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3
+ Nzc3Nzg3JEEAAAAAAAAAAAAAAAAAACQ4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4JEEAAAAA
+ AAAAAAAAAAAAACQ4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4JEEAAAAAAAAAAAAAAAAAACQ5
+ OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5JEEAAAAAAAAAAAAAAAAAACQ6Ojo6Ojo6Ojo6Ojo6
+ Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6JEEAAAAAAAAAAAAAAAAAACQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7
+ Ozs7Ozs7JEEAAAAAAAAAAAAAAAAAACQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7JEEAAAAA
+ AAAAAAAAAAAAACQ8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8JEEAAAAAAAAAAAAAAAAAACQ9
+ PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09JEEAAAAAAAAAAAAAAAAAACQ9PT09PT09PT09PT09
+ PT09PT09PT09PT09PT09PT09JEEAAAAAAAAAAAAAAAAAACQ+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+
+ Pj4+Pj4+JEEAAAAAAAAAAAAAAAAAACQ/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/JEEAAAAA
+ AAAAAAAAAAAAACQ/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/JEEAAAAAAAAAAAAAAAAAACRA
+ QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAJEEAAAAAAAAAAAAAAAAAACQkJCQkJCQkJCQkJCQk
+ JCQkJCQkJCQkJCQkJCQkJCQkJEMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAER///////4O7v///////w7u//8AAAP/Du7//wAAA/8O7v//AAAD/w7u//8AAAP/Du7//wAA
+ A/8O7v//AAAD/w7u//8AAAP/Du7//wAAA/8O7oADAAAD/w7ugAMAAAP/Du6AAwAAA/8O7oADAAAD/w7u
+ gAMAAAP/Du6AAwAAA/8O7oADAAAD/w7ugAMAAAP/Du6AA/////8O7oAAAAAAAA7ugAAAAAAADu6AAAAA
+ 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
+ HBwcHBwcHBwcHBwcHBwcHBwcEBktCxYWFhYWFgsQHR0dHR0dHR0dHR0dHR0dHR0dHR0QGS0LCwsLCwsL
+ CxAeHh4eHh4eHh4eHh4eHh4eHh4eHhAZAAAAAAAAAAAAEB8fHx8fHx8fHx8fHx8fHx8fHx8fEBkAAAAA
+ AAAAAAAQICAgICAgICAgICAgICAgICAgICAQGQAAAAAAAAAAABAhISEhISEhISEhISEhISEhISEhIRAZ
+ AAAAAAAAAAAAECIiIiIiIiIiIiIiIiIiIiIiIiIiEBkAAAAAAAAAAAAQIyMjIyMjIyMjIyMjIyMjIyMj
+ IyMQGQAAAAAAAAAAABAkJCQkJCQkJCQkJCQkJCQkJCQkJBAZAAAAAAAAAAAAECUkJSQlJCQkJCQkJCQk
+ JCQkJCQkEBkAAAAAAAAAAAAQJSUlJSUlJSUlJSUlJSUlJSUlJSUQGQAAAAAAAAAAABAmJiYmJiYmJiYm
+ JiYmJiYmJiYmJhAZAAAAAAAAAAAAECcnJycnJycnJycnJycnJycnJycnEBkAAAAAAAAAAAAQKCgoKCgo
+ KCgoKCgoKCgoKCgoKCgQGQAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQEBArAAAAAAAAAAAALy4u
+ Li4uLi4uLi4uLi4uLi4uLi4uLjD//////+AAf//gAD//4AA//+AAP//gAD//4AA/AGAAPwAgAD8AIAA/
+ ACAAPwAgAD8AP///AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/4AAAP+AAAD/gAAA/4AAAP+AAAD/gAAA
+ /4AAAP+AAAD/gAAA/4AAAP+AAAD/gAAA/4AAACgAAAAQAAAAIAAAAAEACAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAoj8AAK5OAAC6XQAAr2MPAMZtAACqbEwAsHNMALZ7TACwflQAvINMALKDbwC2h28A
+ uYxvAL2RbwC2jnQAADerAAA8tgAQUL8AQGi9AABCwgAAR80AEFPEAABM2AAlW8AAQGrDAD6w3wA+tuEA
+ PrzkAHiGwgB4iMUAcIzKAHiJyQB4jM0AZI3QAHCT2QBftdwAX7neAGCt2ABgstoAR7zmAF+/4QA9wucA
+ PcjqADzO7QA81O8AO9ryAEfD6QBGyuwAXsLhAEbR7wBG2PIARd/1AEXl+ABE7PwAu7zZALu93ACBuuAA
+ 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
+ ADAAAABgAAAAAQAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7i4gAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7g
+ 4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAGYzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2Yz
+ M2YzM2YzM2YzM2YzM2YzM2YzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM5o5A546AJ46AJ46AJ46AJ46AJ46
+ AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AJ46AGYzMwAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AGYzM54+A6I/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/AKI/
+ AKI/AGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM6JCA6ZDAKZDAKZDAKZDAKZDAKZDAKZDAKZDAKZDAKZD
+ AKZDAKZDAKZDAKZDAKZDAKZDAKZDAKZDAKZDAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM6ZHA6pJAKpJ
+ AKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAKpJAGYzMwAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAGYzM6lMA65OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5OAK5O
+ AK5OAK5OAK5OAK5OAK5OAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM61RA7JTALJTALJTALJTALJTALJT
+ ALJTALJTALJTALJTALJTALJTALJTALJTALJTALJTALJTALJTALJTAGYzMwAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AGYzM7FWA7ZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZALZZ
+ ALZZAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQAzoQAzoQAzoQAzoQAzoQAz
+ oQAzoQAzoQAzoQAzoQAzoQAzoQAAAAAAAGYzM7RaA7pdALpdALpdALpdALpdALpdALpdALpdALpdALpd
+ ALpdALpdALpdALpdALpdALpdALpdALpdALpdAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAzoQA0owA0pAA0pAA0pAA0pAA0pAA0pAA0pAA0pAA0pAA0pAAzoQAAAAAAAGYzM7hfA75iAL5i
+ AL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAL5iAGYzMwAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA2pwA2qAA2qAA2qAA2qAA2qAA2qAA2qAA2qAA2
+ qAA2qAAzoQAAAAAAAGYzM7xkA8JoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJoAMJo
+ AMJoAMJoAMJoAMJoAMJoAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA3qwA3
+ qwA3qwA3qwA3qwA3qwA3qwA3qwA3qwA3qwA3qwAzoQAAAAAAAGYzM8BpA8ZtAMZtAMZtAMZtAMZtAMZt
+ AMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAMZtAGYzMwAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAzoQA5rgA5rwA5rwA5rwA5rwA5rwA5rwA5rwA5rwA5rwA5rwAzoQAAAAAA
+ AGYzM8NuA8pyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpyAMpy
+ AMpyAGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA6sQA7swA7swA7swA7swA7
+ swA7swA7swA7swA7swA7swAzoQAAAAAAAGYzM8dzA853AM53AM53AM53AM53AM53AM53AM53AM53AM53
+ AM53AM53AM53AM53AM53AM53AM53AM53AM53AGYzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAzoQA8tQA8tgA8tgA8tgA8tgA8tgA8tgA8tgA8tgA8tgA8tgAzoQAAAAAAAGYzM8t3A9J8ANJ8
+ ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8ANJ8AGYzMwAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA9uAA+ugA+ugA+ugA+ugA+ugA+ugA+ugA+ugA+
+ ugA+ugAzoQAAAAAAAGYzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2YzM2Yz
+ M2YzM2YzM2YzM2YzM2YzM2YzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoQA/vABA
+ vgBAvgBAvgBAvgBAvgBAvgBAvgBAvgBAvgBAvgAzoQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAzoQBBvwBBwQBBwQBBwQBBwQBBwQBBwQBBwQBBwQBBwQBBwQAzoQBbrwBb
+ rwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBb
+ rwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbr7rH3wAAAAAzoQBCwwBDxQBDxQBDxQBDxQBD
+ xQBDxQBDxQBDxQBDxQBDxQAzoQBbr0i45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei4
+ 5Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45Ei45ABbr6y8
+ 2QAAAAAzoQBExgBFyQBFyQBFyQBFyQBFyQBFyQBFyQBFyQBFyQBFyQAzoQBbr0i65Ui65Ui65Ui65Ui6
+ 5Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui65Ui6
+ 5Ui65Ui65Ui65Ui65Ui65Ui65QBbr6y82QAAAAAzoQBFygBHzABHzABHzABHzABHzABHzABHzABHzABH
+ zABHzAAzoQBbr0i85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki8
+ 5ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85ki85gBbr6y82QAAAAAzoQBHzQBI
+ 0ABI0ABI0ABI0ABI0ABI0ABI0ABI0ABI0ABI0AAzoQBbr0i/50i/50i/50i/50i/50i/50i/50i/50i/
+ 50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/50i/
+ 50i/50i/5wBbr6y82QAAAAAzoQBI0QBK1ABK1ABK1ABK1ABK1ABK1ABK1ABK1ABK1ABK1AAzoQBbr0fB
+ 6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB
+ 6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6EfB6ABbr6y82QAAAAAzoQBK1ABM2ABM2ABM2ABM2ABM
+ 2ABM2ABM2ABM2ABM2ABM2AAzoQBbr0fD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD
+ 6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6UfD6QBbr6y8
+ 2QAAAAAzoQBM2ABN2wBN2wBN2wBN2wBN2wBN2wBN2wBN2wBN2wBN2wAzoQBbr0fF6kfF6kfF6kfF6kfF
+ 6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF6kfF
+ 6kfF6kfF6kfF6kfF6kfF6kfF6gBbr6y82QAAAAAzoQBGzABIzwBIzwBIzwBIzwBIzwBIzwBIzwBIzwBI
+ zwBIzwAzoQBbr0fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI
+ 60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI60fI6wBbr6y82QAAAAAzoQAzoQAz
+ oQAzoQAzoQAzoQAzoQAzoQAzoQAzoQAzoQAzoQAzoQBbr0fK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK
+ 7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK7EfK
+ 7EfK7EfK7ABbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0fM
+ 7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM
+ 7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7UfM7QBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAABbr0bO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO
+ 7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7kbO7gBbr6y8
+ 2QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0bR70bR70bR70bR70bR
+ 70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR70bR
+ 70bR70bR70bR70bR70bR70bR7wBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAABbr0bT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT
+ 8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8EbT8ABbr6y82QAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0bV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV
+ 8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV8kbV
+ 8kbV8kbV8gBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0bX
+ 80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX
+ 80bX80bX80bX80bX80bX80bX80bX80bX80bX80bX8wBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAABbr0ba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba
+ 9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9Eba9ABbr6y8
+ 2QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xc9UXc9UXc9UXc9UXc
+ 9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc9UXc
+ 9UXc9UXc9UXc9UXc9UXc9UXc9QBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAABbr0Xe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe
+ 9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9kXe9gBbr6y82QAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg
+ 90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg90Xg
+ 90Xg90Xg9wBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xj
+ +EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj
+ +EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+EXj+ABbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl
+ +UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+UXl+QBbr6y8
+ 2QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Xn+kXn+kXn+kXn+kXn
+ +kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn+kXn
+ +kXn+kXn+kXn+kXn+kXn+kXn+gBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAABbr0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp
+ +0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+0Tp+wBbr6y82QAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Ts/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs
+ /ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs/ETs
+ /ETs/ETs/ABbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr0Tu
+ /UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu
+ /UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/UTu/QBbr6y82QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAABbr0Tw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw
+ /kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/kTw/gBbr669
+ 2QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbrwBbrwBbrwBbrwBbrwBb
+ rwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBbrwBb
+ rwBbrwBbrwBbrwBbrwBbrwBbrwBbr+XZ4gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7i4n///////g7u////
+ ////Du7//wAAA/8O7v//AAAD/w7u//8AAAP/Du7//wAAA/8O7v//AAAD/w7u//8AAAP/Du7//wAAA/8O
+ 7v//AAAD/w7ugAMAAAP/Du6AAwAAA/8O7oADAAAD/w7ugAMAAAP/Du6AAwAAA/8O7oADAAAD/w7ugAMA
+ AAP/Du6AAwAAA/8O7oAD/////w7ugAAAAAAADu6AAAAAAAAO7oAAAAAAAA7ugAAAAAAADu6AAAAAAAAO
+ 7oAAAAAAAA7ugAAAAAAADu6AAAAAAAAO7oAAAAAAAA7ugAAAAAAADu7//AAAAAAO7v/8AAAAAA7u//wA
+ AAAADu7//AAAAAAO7v/8AAAAAA7u//wAAAAADu7//AAAAAAO7v/8AAAAAA7u//wAAAAADu7//AAAAAAO
+ 7v/8AAAAAA7u//wAAAAADu7//AAAAAAO7v/8AAAAAA7u//wAAAAADu7//AAAAAAO7v/8AAAAAA7u//wA
+ AAAADu7///////4O7igAAAAgAAAAQAAAAAEAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AABmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzMAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmMzOfOwCfOwCfOwCfOwCfOwCfOwCf
+ OwCfOwCfOwCfOwCfOwCfOwBmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAABmMzOkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQCkQQBmMzOxmJgA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmMzOrSgCrSgCr
+ SgCrSgCrSgCrSgCrSgCrSgCrSgCrSgCrSgCrSgBmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmMzOwUQCwUQCwUQCwUQCwUQCwUQCwUQCwUQCwUQCwUQCw
+ UQCwUQBmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AABmMzO3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgC3WgBmMzOxmJgAAAAAAAAAAAAAAAAA
+ AAAAAADHu9QAM6EAM6EAM6EAM6EAM6EAM6EAM6EAM6EAAAAAAABmMzO8YAC8YAC8YAC8YAC8YAC8YAC8
+ YAC8YAC8YAC8YAC8YAC8YABmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAADHu9QAM6EANqkANqkANqkANqkA
+ NqkANqkAM6F/mM8AAABmMzPDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQDDaQBmMzOxmJgA
+ AAAAAAAAAAAAAAAAAAAAAADHu9QAM6EAOK0AOK0AOK0AOK0AOK0AOK0AM6F/mM8AAABmMzPIcADIcADI
+ cADIcADIcADIcADIcADIcADIcADIcADIcADIcABmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAADHu9QAM6EA
+ O7QAO7QAO7QAO7QAO7QAO7QAM6F/mM8AAABmMzPPeADPeADPeADPeADPeADPeADPeADPeADPeADPeADP
+ eADPeABmMzOxmJgAAAAAAAAAAAAAAAAAAAAAAADHu9QAM6EAPbgAPbgAPbgAPbgAPbgAPbgAM6F/mM8A
+ AABmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzNmMzOxmJgAAAAAAAAAAAAAAAAA
+ AAAAAADHu9QAM6EAQL8AQL8AQL8AQL8AQL8AQL8AM6FVfMEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADHu9QAM6EAQsMAQsMAQsMAQsMA
+ QsMAQsMAM6EAW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68AW68A
+ W68AW68AW68AW68AW6+BpM7Hu9QAM6EARcoARcoARcoARcoARcoARcoAM6EAW69IuuVIuuVIuuVIuuVI
+ uuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuVIuuUAW698oMzHu9QAM6EA
+ R84AR84AR84AR84AR84AR84AM6EAW69IvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZIvuZI
+ vuZIvuZIvuZIvuZIvuZIvuZIvuZIvuYAW698oMzHu9QAM6EAStUAStUAStUAStUAStUAStUAM6EAW69H
+ wehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwehHwegA
+ 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
+ SH08XNsEAFaMAdioj++z4Eh9MkLbBABWjAHYsLfvs+BIzR6pbQIAK8YAbEAAYqPmEACUwBiAFr18nwVH
+ au4obRMAWDEGYP2evs+CI/XpaG0TAFgxBmC9Hr7PgiM1b4y2CQCsGAOwLgGIjfqMAKAExgCss6fvs+BI
+ fT5W2wQAVowBaL6H77PgSH0xTtsEAFaMAWjW3fdZcKS+HK9tAgArxgA07e77LDhS88drmwDAijEAa3fz
+ fRYcqQVV2iYAsGIMQJOuvs+CI/XVBG0TAFgxBmCtLr7PgiP19URtEwBYIQDlgQCgJMYArNnZ91lwpL6Z
+ pG0CACvGAKzRyfdZcKQWTtY2AYAVYwBW7+j7LDhSi6q1TQBgxRiA1QhAbNRiAoASGAPQuIPvs+BILZmi
+ bQIAK8YArNre91lwpL6t0TYBgBVjAFZp5/ssOFLfTdU2AYAVYwBWJgCxUd8TAJTAGIBGbX2fBUfqh2na
+ JgCwYgzASm18nwVHaul0bRMAWDEFAPEjALBCAMoTAYAVAlCeCACsEIDyRAAARI8AAAkjAEDCCACQMAIA
+ JIwAAAkjAEDCCACQMFMAAKRlbv1PNdVF4Jm637at+5rUfY3qvrL4iScADfsZgOX03tj+IOMAAAAASUVO
+ RK5CYIIoAAAAMAAAAGAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/svLi/7Ly2D+y8tb/svL
+ W/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svL
+ W/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svL
+ W/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8tb/svLW/7Ly1v+y8uU/svL
+ ev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AGUyMhxlMjIgZTIyIGUyMiBlMjIgZTIyIGUyMiBlMjIgZTIyIGUyMiBlMjIgZTIyIGUyMiBlMjIgZTIy
+ IGUyMiBlMjIgZTIyIGUyMiBlMjIgZTIyIGUyMiBlMjIIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAGYzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz
+ /2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+aOQP/njoA/546AP+eOgD/njoA/546AP+eOgD/njoA
+ /546AP+eOgD/njoA/546AP+eOgD/njoA/546AP+eOgD/njoA/546AP+eOgD/njoA/2YzM/9lMjJAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+ePgP/oj8A/6I/AP+iPwD/oj8A
+ /6I/AP+iPwD/oj8A/6I/AP+iPwD/oj8A/6I/AP+iPwD/oj8A/6I/AP+iPwD/oj8A/6I/AP+iPwD/oj8A
+ /2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+iQgP/pkMA
+ /6ZDAP+mQwD/pkMA/6ZDAP+mQwD/pkMA/6ZDAP+mQwD/pkMA/6ZDAP+mQwD/pkMA/6ZDAP+mQwD/pkMA
+ /6ZDAP+mQwD/pkMA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svL
+ ev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AGYzM/+mRwP/qkkA/6pJAP+qSQD/qkkA/6pJAP+qSQD/qkkA/6pJAP+qSQD/qkkA/6pJAP+qSQD/qkkA
+ /6pJAP+qSQD/qkkA/6pJAP+qSQD/qkkA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAGYzM/+pTAP/rk4A/65OAP+uTgD/rk4A/65OAP+uTgD/rk4A/65OAP+uTgD/rk4A
+ /65OAP+uTgD/rk4A/65OAP+uTgD/rk4A/65OAP+uTgD/rk4A/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+tUQP/slMA/7JTAP+yUwD/slMA/7JTAP+yUwD/slMA
+ /7JTAP+yUwD/slMA/7JTAP+yUwD/slMA/7JTAP+yUwD/slMA/7JTAP+yUwD/slMA/2YzM/9lMjJAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYzM/+xVgP/tlkA/7ZZAP+2WQD/tlkA
+ /7ZZAP+2WQD/tlkA/7ZZAP+2WQD/tlkA/7ZZAP+2WQD/tlkA/7ZZAP+2WQD/tlkA/7ZZAP+2WQD/tlkA
+ /2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AM6H/ADOh
+ /wAzof8AM6H/ADOh/wAzof8AM6H/ADOh/wAzof8AM6H/ADOh/wAzof8AMqAsAAAAAGYzM/+0WgP/ul0A
+ /7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A
+ /7pdAP+6XQD/ul0A/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svL
+ egAzof8ANKP/ADSk/wA0pP8ANKT/ADSk/wA0pP8ANKT/ADSk/wA0pP8ANKT/ADSk/wAzof8AMqBAAAAA
+ AGYzM/+4XwP/vmIA/75iAP++YgD/vmIA/75iAP++YgD/vmIA/75iAP++YgD/vmIA/75iAP++YgD/vmIA
+ /75iAP++YgD/vmIA/75iAP++YgD/vmIA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAD+y8tb/svLegAzof8ANqf/ADao/wA2qP8ANqj/ADao/wA2qP8ANqj/ADao/wA2qP8ANqj/ADao
+ /wAzof8AMqBAAAAAAGYzM/+8ZAP/wmgA/8JoAP/CaAD/wmgA/8JoAP/CaAD/wmgA/8JoAP/CaAD/wmgA
+ /8JoAP/CaAD/wmgA/8JoAP/CaAD/wmgA/8JoAP/CaAD/wmgA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AN6v/ADer/wA3q/8AN6v/ADer/wA3q/8AN6v/ADer
+ /wA3q/8AN6v/ADer/wAzof8AMqBAAAAAAGYzM//AaQP/xm0A/8ZtAP/GbQD/xm0A/8ZtAP/GbQD/xm0A
+ /8ZtAP/GbQD/xm0A/8ZtAP/GbQD/xm0A/8ZtAP/GbQD/xm0A/8ZtAP/GbQD/xm0A/2YzM/9lMjJAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AOa7/ADmv/wA5r/8AOa//ADmv
+ /wA5r/8AOa//ADmv/wA5r/8AOa//ADmv/wAzof8AMqBAAAAAAGYzM//DbgP/ynIA/8pyAP/KcgD/ynIA
+ /8pyAP/KcgD/ynIA/8pyAP/KcgD/ynIA/8pyAP/KcgD/ynIA/8pyAP/KcgD/ynIA/8pyAP/KcgD/ynIA
+ /2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AOrH/ADuz
+ /wA7s/8AO7P/ADuz/wA7s/8AO7P/ADuz/wA7s/8AO7P/ADuz/wAzof8AMqBAAAAAAGYzM//HcwP/zncA
+ /853AP/OdwD/zncA/853AP/OdwD/zncA/853AP/OdwD/zncA/853AP/OdwD/zncA/853AP/OdwD/zncA
+ /853AP/OdwD/zncA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svL
+ egAzof8APLX/ADy2/wA8tv8APLb/ADy2/wA8tv8APLb/ADy2/wA8tv8APLb/ADy2/wAzof8AMqBAAAAA
+ AGYzM//LdwP/0nwA/9J8AP/SfAD/0nwA/9J8AP/SfAD/0nwA/9J8AP/SfAD/0nwA/9J8AP/SfAD/0nwA
+ /9J8AP/SfAD/0nwA/9J8AP/SfAD/0nwA/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAD+y8tb/svLegAzof8APbj/AD66/wA+uv8APrr/AD66/wA+uv8APrr/AD66/wA+uv8APrr/AD66
+ /wAzof8AMqBAAAAAAGYzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz
+ /2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9lMjJAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AP7z/AEC+/wBAvv8AQL7/AEC+/wBAvv8AQL7/AEC+
+ /wBAvv8AQL7/AEC+/wAzof8AMqBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tb/svLegAzof8AQb//AEHB/wBBwf8AQcH/AEHB
+ /wBBwf8AQcH/AEHB/wBBwf8AQcH/AEHB/wAzof8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv
+ /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv
+ /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/+InsiS/svLegAzof8AQsP/AEPF
+ /wBDxf8AQ8X/AEPF/wBDxf8AQ8X/AEPF/wBDxf8AQ8X/AEPF/wAzof8AW6//SLjk/0i45P9IuOT/SLjk
+ /0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk
+ /0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/0i45P9IuOT/SLjk/wBbr/9+mMWk/svL
+ egAzof8ARMb/AEXJ/wBFyf8ARcn/AEXJ/wBFyf8ARcn/AEXJ/wBFyf8ARcn/AEXJ/wAzof8AW6//SLrl
+ /0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl
+ /0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl
+ /wBbr/9+mMWk/svLegAzof8ARcr/AEfM/wBHzP8AR8z/AEfM/wBHzP8AR8z/AEfM/wBHzP8AR8z/AEfM
+ /wAzof8AW6//SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm
+ /0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm/0i85v9IvOb/SLzm
+ /0i85v9IvOb/SLzm/wBbr/9+mMWk/svLegAzof8AR83/AEjQ/wBI0P8ASND/AEjQ/wBI0P8ASND/AEjQ
+ /wBI0P8ASND/AEjQ/wAzof8AW6//SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n
+ /0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n
+ /0i/5/9Iv+f/SL/n/0i/5/9Iv+f/SL/n/wBbr/9+mMWk/svLegAzof8ASNH/AErU/wBK1P8AStT/AErU
+ /wBK1P8AStT/AErU/wBK1P8AStT/AErU/wAzof8AW6//R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho
+ /0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho
+ /0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/wBbr/9+mMWk/8zMegAzof8AStT/AEzY
+ /wBM2P8ATNj/AEzY/wBM2P8ATNj/AEzY/wBM2P8ATNj/AEzY/wAzof8AW6//R8Pp/0fD6f9Hw+n/R8Pp
+ /0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp
+ /0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/0fD6f9Hw+n/R8Pp/wBbr/9+mMWk/8zM
+ egAzof8ATNj/AE3b/wBN2/8ATdv/AE3b/wBN2/8ATdv/AE3b/wBN2/8ATdv/AE3b/wAzof8AW6//R8Xq
+ /0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq
+ /0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq/0fF6v9Hxer/R8Xq
+ /wBbr/9+mMWk/8zMegAzof8ARsz/AEjP/wBIz/8ASM//AEjP/wBIz/8ASM//AEjP/wBIz/8ASM//AEjP
+ /wAzof8AW6//R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr
+ /0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr
+ /0fI6/9HyOv/R8jr/wBbr/9+mMWk/8zMegAzof8AM6H/ADOh/wAzof8AM6H/ADOh/wAzof8AM6H/ADOh
+ /wAzof8AM6H/ADOh/wAzof8AW6//R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs
+ /0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs
+ /0fK7P9Hyuz/R8rs/0fK7P9Hyuz/R8rs/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt
+ /0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt
+ /0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/0fM7f9HzO3/R8zt/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rs7u/0bO7v9Gzu7/Rs7u
+ /0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u
+ /0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/0bO7v9Gzu7/Rs7u/wBbr/9+mMWk/8zM
+ ev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//RtHv
+ /0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv
+ /0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv/0bR7/9G0e//RtHv
+ /wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAW6//RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw
+ /0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw/0bT8P9G0/D/RtPw
+ /0bT8P9G0/D/RtPw/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAW6//RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy
+ /0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy
+ /0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz
+ /0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz
+ /0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/0bX8/9G1/P/Rtfz/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rtr0/0ba9P9G2vT/Rtr0
+ /0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0
+ /0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/0ba9P9G2vT/Rtr0/wBbr/9+mMWk/svL
+ ev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rdz1
+ /0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1
+ /0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1
+ /wBbr/9+mMWk/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAW6//Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72
+ /0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72/0Xe9v9F3vb/Rd72
+ /0Xe9v9F3vb/Rd72/wBbr/9+mMWk/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAW6//ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3
+ /0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3
+ /0Xg9/9F4Pf/ReD3/0Xg9/9F4Pf/ReD3/wBbr/9+mMWk/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4
+ /0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4
+ /0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/wBbr/9+mMWk/svLev7LywgAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//ReX5/0Xl+f9F5fn/ReX5
+ /0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5
+ /0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/0Xl+f9F5fn/ReX5/wBbr/9+mMWk/svL
+ ev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Ref6
+ /0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6
+ /0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6/0Xn+v9F5/r/Ref6
+ /wBbr/9+mMWk/svLev7LywgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAW6//ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7
+ /0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7/0Tp+/9E6fv/ROn7
+ /0Tp+/9E6fv/ROn7/wBbr/9+mMWk/8zMev/MzAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAW6//ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8
+ /0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8
+ /0Ts/P9E7Pz/ROz8/0Ts/P9E7Pz/ROz8/wBbr/9+mMWk/svLev/LywgAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79
+ /0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79
+ /0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/0Tu/f9E7v3/RO79/wBbr/9+mMWk/svLev7LywgAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//RPD+/0Tw/v9E8P7/RPD+
+ /0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+
+ /0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/0Tw/v9E8P7/RPD+/wBbr/+BmMSi/svL
+ ev7Lyw7+y8sI/8zMCP/MzAj+y8sI/svLCP7Lywj+y8sI/svLCP7Lywj+y8sI/8zMCP/MzAgAW6//AFuv
+ /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv
+ /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv
+ /wBbr//NtcaA/svLfP/Ly3r+y8t6/8zMev/MzHr+y8t6/svLev7Ly3r+y8t6/svLev7Ly3r+y8t6/8zM
+ ev/MzHr/zMx6/8zMev/MzHr/zMx6/8zMev/MzHr/zMx6/8zMev/MzHr+y8t6/svLev7Ly3r+y8t6/svL
+ ev7Ly3r+y8t6/svLev7Ly3r+y8t6/svLev7Ly3r+y8t6/svLev7Ly3r+y8t6/svLev7Ly3r+y8t6/svL
+ ev7Ly3r+y8t6/svLev7Ly3r+y8uLAAAAAAAADu4//wAAAf4O7j//AAAB/g7uP/8AAAH+Du4//wAAAf4O
+ 7j//AAAB/g7uP/8AAAH+Du4//wAAAf4O7j//AAAB/g7uP/8AAAH+Du4AAQAAAf4O7gABAAAB/g7uAAEA
+ AAH+Du4AAQAAAf4O7gABAAAB/g7uAAEAAAH+Du4AAQAAAf4O7gABAAAB/g7uAAH////+Du4AAAAAAAAO
+ 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
+ PamYvaYAM6H/AEC//wBAv/8AQL//AEC//wBAv/8AQL//ADOh/wA7o6oAW69VAFuvVQBbr1UAW69VAFuv
+ VQBbr1UAW69VAFuvVQBbr1UAW69VAFuvVQBbr1UAW69VAFuvVQBbr1UAW69VAFuvVQBbr1UAW69VAFuv
+ VQBbr1WsqMRlqZi9pgAzof8AQsP/AELD/wBCw/8AQsP/AELD/wBCw/8AM6H/AFuv/wBbr/8AW6//AFuv
+ /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv
+ /wBbr/8AW6//AFuv/1aFvr6pmL2mADOh/wBFyv8ARcr/AEXK/wBFyv8ARcr/AEXK/wAzof8AW6//SLrl
+ /0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl/0i65f9IuuX/SLrl
+ /0i65f9IuuX/SLrl/0i65f8AW6//VIO9wqmYvaYAM6H/AEfO/wBHzv8AR87/AEfO/wBHzv8AR87/ADOh
+ /wBbr/9Ivub/SL7m/0i+5v9Ivub/SL7m/0i+5v9Ivub/SL7m/0i+5v9Ivub/SL7m/0i+5v9Ivub/SL7m
+ /0i+5v9Ivub/SL7m/0i+5v9Ivub/SL7m/wBbr/9Ug73CqZi9pgAzof8AStX/AErV/wBK1f8AStX/AErV
+ /wBK1f8AM6H/AFuv/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho
+ /0fB6P9Hwej/R8Ho/0fB6P9Hwej/R8Ho/0fB6P9Hwej/AFuv/1SDvcKqmb2mADOh/wBM2v8ATNr/AEza
+ /wBM2v8ATNr/AEza/wAzof8AW6//R8Tp/0fE6f9HxOn/R8Tp/0fE6f9HxOn/R8Tp/0fE6f9HxOn/R8Tp
+ /0fE6f9HxOn/R8Tp/0fE6f9HxOn/R8Tp/0fE6f9HxOn/R8Tp/0fE6f8AW6//VIO9wqqZvaYAM6H/ADOh
+ /wAzof8AM6H/ADOh/wAzof8AM6H/ADOh/wBbr/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr
+ /0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/0fI6/9HyOv/R8jr/wBbr/9Ug73C4rvH
+ b15rsFcAM6FVADOhVQAzoVUAM6FVADOhVQAzoVUAM6FVAFuv/0fL7P9Hy+z/R8vs/0fL7P9Hy+z/R8vs
+ /0fL7P9Hy+z/R8vs/0fL7P9Hy+z/R8vs/0fL7P9Hy+z/R8vs/0fL7P9Hy+z/R8vs/0fL7P9Hy+z/AFuv
+ /1SDvcL/zMxU/8zMAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//Rs/u/0bP7v9Gz+7/Rs/u
+ /0bP7v9Gz+7/Rs/u/0bP7v9Gz+7/Rs/u/0bP7v9Gz+7/Rs/u/0bP7v9Gz+7/Rs/u/0bP7v9Gz+7/Rs/u
+ /0bP7v8AW6//VIO9wv/MzFT/zMwDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr/9G0u//RtLv
+ /0bS7/9G0u//RtLv/0bS7/9G0u//RtLv/0bS7/9G0u//RtLv/0bS7/9G0u//RtLv/0bS7/9G0u//RtLv
+ /0bS7/9G0u//RtLv/wBbr/9Ug73C/8zMVP/MzAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFuv
+ /0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy/0bV8v9G1fL/RtXy
+ /0bV8v9G1fL/RtXy/0bV8v9G1fL/AFuv/1SDvcL/zMxU/8zMAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAW6//Rtnz/0bZ8/9G2fP/Rtnz/0bZ8/9G2fP/Rtnz/0bZ8/9G2fP/Rtnz/0bZ8/9G2fP/Rtnz
+ /0bZ8/9G2fP/Rtnz/0bZ8/9G2fP/Rtnz/0bZ8/8AW6//VIO9wv7Ly1T+y8sDAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAABbr/9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1
+ /0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/0Xc9f9F3PX/Rdz1/wBbr/9Ug73C/svLVP7LywMAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAFuv/0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/Rd/2
+ /0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/Rd/2/0Xf9v9F3/b/AFuv/1SDvcL+y8tU/svL
+ AwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4
+ /0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P9F4/j/ReP4/0Xj+P8AW6//VIO9
+ wv7Ly1T+y8sDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbr/9F5vn/Reb5/0Xm+f9F5vn/Reb5
+ /0Xm+f9F5vn/Reb5/0Xm+f9F5vn/Reb5/0Xm+f9F5vn/Reb5/0Xm+f9F5vn/Reb5/0Xm+f9F5vn/Reb5
+ /wBbr/9Ug73C/svLVP7LywMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFuv/0Tq+/9E6vv/ROr7
+ /0Tq+/9E6vv/ROr7/0Tq+/9E6vv/ROr7/0Tq+/9E6vv/ROr7/0Tq+/9E6vv/ROr7/0Tq+/9E6vv/ROr7
+ /0Tq+/9E6vv/AFuv/1SDvcL+y8tU/8vLAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW6//RO38
+ /0Tt/P9E7fz/RO38/0Tt/P9E7fz/RO38/0Tt/P9E7fz/RO38/0Tt/P9E7fz/RO38/0Tt/P9E7fz/RO38
+ /0Tt/P9E7fz/RO38/0Tt/P8AW6//VIO9wv7Ly1X+y8sF/8zMA/7LywP+y8sD/svLA/7LywP+y8sD/8zM
+ AwBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv
+ /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/9mir25/svLb/7Ly1X/zMxU/svLVP7Ly1T+y8tU/svL
+ VP7Ly1T/zMxUxrLFi6qmwqaqpsKmqqbCpqqmwqaqpsKmqaXBpqmlwaappcGmqaXBpqmlwaappcGmqaXB
+ pqmlwaappcGmqaXBpqmlwaappcGmqaXBpqmlwaappcGmqaXBpta5xpIAAAAAP8AAPj/AAD4/wAA+P8AA
+ Pj/AAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAAAAAAAAAAAACgAAAAQAAAAIAAA
+ AAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+y8tB/svLHv7Lyx7+y8se/svLHp1qanCYZWWjmGVl
+ o5hlZaOYZWWjmGVlo5hlZaOYZWV4/svLHv7Lyx7+y8s5/svLLAAAAAAAAAAAAAAAAAAAAABmMzP/ZjMz
+ /2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/wAAAAAAAAAA/svLHv7LyywAAAAAAAAAAAAAAAAAAAAAZjMz
+ /61NAP+tTQD/rU0A/61NAP+tTQD/rU0A/2YzM/8AAAAAAAAAAP7Lyx4AM6H/ADOh/wAzof8AM6H/ADOh
+ /2YzM/+5XQD/uV0A/7ldAP+5XQD/uV0A/7ldAP9mMzP/AAAAAAAAAAD+y8seADOh/wA3q/8AN6v/ADer
+ /wAzof9mMzP/xWwA/8VsAP/FbAD/xWwA/8VsAP/FbAD/ZjMz/wAAAAAAAAAA/svLHgAzof8APLb/ADy2
+ /wA8tv8AM6H/ZjMz/2YzM/9mMzP/ZjMz/2YzM/9mMzP/ZjMz/2YzM/8AAAAAAAAAAP7Lyx4AM6H/AEHB
+ /wBBwf8AQcH/ADOh/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//ADOh
+ /wBGzP8ARsz/AEbM/wAzof9IvOX/SLzl/0i85f9IvOX/SLzl/0i85f9IvOX/SLzl/0i85f9IvOX/AFuv
+ /wAzof8AS9f/AEvX/wBL1/8AM6H/R8Lo/0fC6P9Hwuj/R8Lo/0fC6P9Hwuj/R8Lo/0fC6P9Hwuj/R8Lo
+ /wBbr/8AM6H/ADOh/wAzof8AM6H/ADOh/0fJ6/9Hyev/R8nr/0fJ6/9Hyev/R8nr/0fJ6/9Hyev/R8nr
+ /0fJ6/8AW6///8zMLAAAAAAAAAAAAAAAAABbr/9G0O7/RtDu/0bQ7v9G0O7/RtDu/0bQ7v9G0O7/RtDu
+ /0bQ7v9G0O7/AFuv///MzCwAAAAAAAAAAAAAAAAAW6//Rtfy/0bX8v9G1/L/Rtfy/0bX8v9G1/L/Rtfy
+ /0bX8v9G1/L/Rtfy/wBbr//+y8ssAAAAAAAAAAAAAAAAAFuv/0Xd9f9F3fX/Rd31/0Xd9f9F3fX/Rd31
+ /0Xd9f9F3fX/Rd31/0Xd9f8AW6///svLLAAAAAAAAAAAAAAAAABbr/9F5Pj/ReT4/0Xk+P9F5Pj/ReT4
+ /0Xk+P9F5Pj/ReT4/0Xk+P9F5Pj/AFuv//7LyywAAAAAAAAAAAAAAAAAW6//ROv7/0Tr+/9E6/v/ROv7
+ /0Tr+/9E6/v/ROv7/0Tr+/9E6/v/ROv7/wBbr//+y8tI/svLLP7Lyyz+y8ssAFuv/wBbr/8AW6//AFuv
+ /wBbr/8AW6//AFuv/wBbr/8AW6//AFuv/wBbr/8AW6//AACsQXgGrEF4BqxBAAasQQAGrEEABqxBAACs
+ QQAArEEAAKxBAACsQXAArEFwAKxBcACsQXAArEFwAKxBAACsQQ==
+
+
+
\ No newline at end of file
diff --git a/branches/ph-plugins/ExtraTools/SysCallHacker/Program.cs b/branches/ph-plugins/ExtraTools/SysCallHacker/Program.cs
new file mode 100644
index 000000000..fcabcf5db
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/SysCallHacker/Program.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Windows.Forms;
+
+namespace SysCallHacker
+{
+ static class Program
+ {
+ ///
+ /// The main entry point for the application.
+ ///
+ [STAThread]
+ static void Main()
+ {
+ Application.EnableVisualStyles();
+ Application.SetCompatibleTextRenderingDefault(false);
+ Application.Run(new MainWindow());
+ }
+ }
+}
diff --git a/branches/ph-plugins/ExtraTools/SysCallHacker/Properties/AssemblyInfo.cs b/branches/ph-plugins/ExtraTools/SysCallHacker/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..f48626a0f
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/SysCallHacker/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("SysCallHacker")]
+[assembly: AssemblyDescription("System Call Hacker")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("wj32")]
+[assembly: AssemblyProduct("SysCallHacker")]
+[assembly: AssemblyCopyright("Copyright © 2009 wj32. Licensed under the GNU GPL, v3.")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("55039872-9923-42d3-893d-2dd59e582765")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/branches/ph-plugins/ExtraTools/SysCallHacker/Properties/Resources.Designer.cs b/branches/ph-plugins/ExtraTools/SysCallHacker/Properties/Resources.Designer.cs
new file mode 100644
index 000000000..d6a9b8ec8
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/SysCallHacker/Properties/Resources.Designer.cs
@@ -0,0 +1,71 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:2.0.50727.4016
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace SysCallHacker.Properties
+{
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources
+ {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources()
+ {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager
+ {
+ get
+ {
+ if ((resourceMan == null))
+ {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SysCallHacker.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture
+ {
+ get
+ {
+ return resourceCulture;
+ }
+ set
+ {
+ resourceCulture = value;
+ }
+ }
+ }
+}
diff --git a/branches/ph-plugins/ExtraTools/SysCallHacker/Properties/Resources.resx b/branches/ph-plugins/ExtraTools/SysCallHacker/Properties/Resources.resx
new file mode 100644
index 000000000..ffecec851
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/SysCallHacker/Properties/Resources.resx
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/branches/ph-plugins/ExtraTools/SysCallHacker/Properties/Settings.Designer.cs b/branches/ph-plugins/ExtraTools/SysCallHacker/Properties/Settings.Designer.cs
new file mode 100644
index 000000000..9f286282d
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/SysCallHacker/Properties/Settings.Designer.cs
@@ -0,0 +1,30 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:2.0.50727.4016
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace SysCallHacker.Properties
+{
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "9.0.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
+ {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default
+ {
+ get
+ {
+ return defaultInstance;
+ }
+ }
+ }
+}
diff --git a/branches/ph-plugins/ExtraTools/SysCallHacker/Properties/Settings.settings b/branches/ph-plugins/ExtraTools/SysCallHacker/Properties/Settings.settings
new file mode 100644
index 000000000..abf36c5d3
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/SysCallHacker/Properties/Settings.settings
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/branches/ph-plugins/ExtraTools/SysCallHacker/SysCallHacker.csproj b/branches/ph-plugins/ExtraTools/SysCallHacker/SysCallHacker.csproj
new file mode 100644
index 000000000..3da8365d5
--- /dev/null
+++ b/branches/ph-plugins/ExtraTools/SysCallHacker/SysCallHacker.csproj
@@ -0,0 +1,102 @@
+
+
+
+ Debug
+ AnyCPU
+ 9.0.30729
+ 2.0
+ {39B5CDC9-0AB3-4E1F-862C-EA95BC5A0715}
+ WinExe
+ Properties
+ SysCallHacker
+ SysCallHacker
+ v2.0
+ 512
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ AnyCPU
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ AnyCPU
+
+
+
+
+
+
+
+
+
+
+
+ Form
+
+
+ EventProperties.cs
+
+
+
+ Form
+
+
+ MainWindow.cs
+
+
+
+
+ EventProperties.cs
+
+
+ MainWindow.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+ Designer
+
+
+ True
+ Resources.resx
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+ True
+ Settings.settings
+ True
+
+
+
+
+ {8E10F5E8-D4FA-4980-BB23-2EDD134AC15E}
+ ProcessHacker.Common
+
+
+ {8A448157-E1A7-4DDF-954E-287F1117832B}
+ ProcessHacker.Native
+
+
+
+
+
\ No newline at end of file
diff --git a/branches/ph-plugins/HACKING.txt b/branches/ph-plugins/HACKING.txt
new file mode 100644
index 000000000..4422509e3
--- /dev/null
+++ b/branches/ph-plugins/HACKING.txt
@@ -0,0 +1,19 @@
+Note to SVN users:
+If you use SVN code you may encounter weird bugs. Please use releases instead.
+
+Process Hacker is developed using Visual Studio 2008, and
+will only work with C# compilers which support C# 3.0. It has been tested on
+Visual Studio 2008 and Visual C# Express Edition (free).
+
+To build KProcessHacker, you will need the Windows DDK.
+To build NProcessHacker, you will need Visual Studio 2008 or Visual C++
+Express Edition (free).
+
+IMPORTANT: If you are using Visual C# Express Edition to compile/run
+Process Hacker, you MUST enable "Show advanced build configurations" in
+Tools > Options > Projects and Solutions > General.
+
+The build script relies on ILMerge being present in the default installation
+location ("%PROGRAMFILES%\Microsoft\ILMerge\ILMerge.exe") or in your PATH
+environment variable. In order to build the installer you must have Inno Setup
+QuickStart Pack installed (v5.3.5+).
\ No newline at end of file
diff --git a/branches/ph-plugins/KProcessHacker/HACKING.txt b/branches/ph-plugins/KProcessHacker/HACKING.txt
new file mode 100644
index 000000000..00c104cfa
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/HACKING.txt
@@ -0,0 +1,67 @@
+==== KProcessHacker ====
+
+== IMPORTANT ==
+KProcessHacker has been developed from either reverse engineering of
+the Windows kernel or ReactOS code (http://www.reactos.org). The
+following files contain "ported" ReactOS code (with modifications):
+
+ * mm.c
+ * MiDoMappedCopy
+ * MiDoPoolCopy (added smarter buffer management)
+ * MiGetExceptionInfo
+ * ps.c
+ * KphOpenProcess
+ * KphOpenThread
+ * se.c
+ * KphOpenProcessTokenEx
+
+== CODE STRUCTURE ==
+ * handle.c
+ - Contains handle table code.
+ * hook.c
+ - Contains hooking code. Currently you may hook any kernel-mode
+ function and object type open procedures.
+ * io.c
+ - Contains I/O-related code, such as device and driver functions.
+ * kph.c
+ - Contains support routines.
+ * kprocesshacker.c
+ - Contains interfacing code, mainly consisting of the I/O control
+ handler.
+ * mm.c
+ - Contains memory-related code, such as reading and writing.
+ * ob.c
+ - Contains object-related code, such as handle duplication.
+ * protect.c
+ - Contains process protection code. Process protection is
+ achieved by hooking ObOpenObjectByPointer and some object type
+ OpenProcedures.
+ * ps.c
+ - Contains process- and thread-related code, such as opening and
+ terminating.
+ * ref.c
+ - Contains the KPH object manager.
+ * se.c
+ - Contains security-related code. Only function there is
+ KphOpenProcessTokenEx.
+ * sync.c
+ - Various synchronization functions.
+ * sysservice.c
+ - System service logging.
+ * trace.c
+ - Stack trace code.
+ * version.c
+ - Contains Windows-version-specific data.
+
+== POOL TAGS ==
+PhAB: System service logging argument block. sysservice.h
+PhCH: Client handle table. kprocesshacker.h
+PhCt: System service logging argument capture temporary buffer. sysservicep.h
+PhCU: Captured Unicode string. kph.h
+PhEB: System service logging event block. sysservice.h
+PhOb: Object manager object. refp.h
+PhPC: Pool-based virtual memory copying. mm.h
+PhPr: Protection entry. protect.h
+PhSc: System service call entry. sysservicedata.h
+PhSD: Processor lock DPC storage. sync.h
+PhSt: Stack back trace. ps.h
diff --git a/branches/ph-plugins/KProcessHacker/amd64/kprocesshacker.sys b/branches/ph-plugins/KProcessHacker/amd64/kprocesshacker.sys
new file mode 100644
index 000000000..9c86f5624
Binary files /dev/null and b/branches/ph-plugins/KProcessHacker/amd64/kprocesshacker.sys differ
diff --git a/branches/ph-plugins/KProcessHacker/auto.cmd b/branches/ph-plugins/KProcessHacker/auto.cmd
new file mode 100644
index 000000000..28cde9736
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/auto.cmd
@@ -0,0 +1,7 @@
+@echo off
+
+build -cZ
+if not %errorlevel%==0 goto end
+copy i386\kprocesshacker.sys ..\ProcessHacker\bin\Release\
+copy i386\kprocesshacker.pdb ..\ProcessHacker\bin\Release\
+:end
\ No newline at end of file
diff --git a/branches/ph-plugins/KProcessHacker/autoreload.cmd b/branches/ph-plugins/KProcessHacker/autoreload.cmd
new file mode 100644
index 000000000..b1e09e9c1
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/autoreload.cmd
@@ -0,0 +1,2 @@
+@echo off
+auto & sc stop kprocesshacker & sc start kprocesshacker
\ No newline at end of file
diff --git a/branches/ph-plugins/KProcessHacker/handle.c b/branches/ph-plugins/KProcessHacker/handle.c
new file mode 100644
index 000000000..43b94fd9a
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/handle.c
@@ -0,0 +1,355 @@
+/*
+ * Process Hacker Driver -
+ * handle table
+ *
+ * Copyright (C) 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 .
+ */
+
+#include "include/handle.h"
+#include "include/handlep.h"
+
+NTSTATUS KphpAllocateHandleEntry(
+ __in PKPH_HANDLE_TABLE HandleTable,
+ __out PKPH_HANDLE_TABLE_ENTRY *Entry
+ );
+
+NTSTATUS KphpFreeHandleEntry(
+ __in PKPH_HANDLE_TABLE HandleTable,
+ __in PKPH_HANDLE_TABLE_ENTRY Entry
+ );
+
+/* KphCreateHandleTable
+ *
+ * Creates a handle table.
+ *
+ * HandleTable: A variable which receives a pointer to the handle table.
+ * MaximumHandles: The maximum number of handles that can be created.
+ * SizeOfEntry: The size of each handle table entry. This value must be
+ * divisible by 4.
+ * Tag: The tag to use when allocating handle table resources.
+ */
+NTSTATUS KphCreateHandleTable(
+ __out PKPH_HANDLE_TABLE *HandleTable,
+ __in ULONG MaximumHandles,
+ __in ULONG SizeOfEntry,
+ __in ULONG Tag
+ )
+{
+ PKPH_HANDLE_TABLE handleTable;
+
+ /* Each handle entry must be at least the size of our
+ * handle table entry definition.
+ */
+ if (SizeOfEntry < sizeof(KPH_HANDLE_TABLE_ENTRY))
+ return STATUS_INVALID_PARAMETER_3;
+ /* Handle entries must be 4-byte aligned. */
+ if (SizeOfEntry % 4 != 0)
+ return STATUS_INVALID_PARAMETER_3;
+
+ /* Allocate storage for the handle table structure. */
+ handleTable = ExAllocatePoolWithTag(
+ PagedPool,
+ sizeof(KPH_HANDLE_TABLE),
+ Tag
+ );
+
+ if (!handleTable)
+ return STATUS_INSUFFICIENT_RESOURCES;
+
+ /* Allocate storage for the handle table itself. */
+ handleTable->Table = ExAllocatePoolWithTag(
+ PagedPool,
+ MaximumHandles * SizeOfEntry,
+ Tag
+ );
+
+ if (!handleTable->Table)
+ {
+ ExFreePoolWithTag(handleTable, Tag);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ /* Initialize the rest of the table descriptor. */
+ handleTable->Tag = Tag;
+ handleTable->SizeOfEntry = SizeOfEntry;
+ handleTable->NextHandle = (HANDLE)0;
+ handleTable->FreeHandle = NULL;
+ ExInitializeFastMutex(&handleTable->Mutex);
+ handleTable->TableSize = MaximumHandles * SizeOfEntry;
+
+ /* Zero the handle table. */
+ memset(handleTable->Table, 0, handleTable->TableSize);
+
+ /* Pass the pointer to the handle table back. */
+ *HandleTable = handleTable;
+
+ return STATUS_SUCCESS;
+}
+
+/* KphFreeHandleTable
+ *
+ * Frees all handle table resources.
+ */
+VOID KphFreeHandleTable(
+ __in PKPH_HANDLE_TABLE HandleTable
+ )
+{
+ ULONG i;
+ ULONG tag;
+
+ /* Free all handle values. */
+ for (i = 0; i < HandleTable->TableSize / HandleTable->SizeOfEntry; i++)
+ {
+ KphCloseHandle(HandleTable, KphHandleFromIndex(i));
+ }
+
+ /* Save the handle table tag first. */
+ tag = HandleTable->Tag;
+ /* Free the table. */
+ ExFreePoolWithTag(HandleTable->Table, tag);
+ /* Free the descriptor. */
+ ExFreePoolWithTag(HandleTable, tag);
+}
+
+/* KphCloseHandle
+ *
+ * Closes a handle, dereferencing the referenced object.
+ */
+NTSTATUS KphCloseHandle(
+ __in PKPH_HANDLE_TABLE HandleTable,
+ __in HANDLE Handle
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PKPH_HANDLE_TABLE_ENTRY entry;
+ PVOID object;
+
+ if (!KphValidHandle(HandleTable, Handle, &entry))
+ return STATUS_INVALID_HANDLE;
+
+ /* Save a pointer to the object referenced by the handle. */
+ object = entry->Object;
+ /* Free the handle. */
+ status = KphpFreeHandleEntry(HandleTable, entry);
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ /* Dereference the object. */
+ KphDereferenceObject(object);
+
+ return status;
+}
+
+/* KphCreateHandle
+ *
+ * Creates a handle and references an object.
+ */
+NTSTATUS KphCreateHandle(
+ __in PKPH_HANDLE_TABLE HandleTable,
+ __in PVOID Object,
+ __out PHANDLE Handle
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PKPH_HANDLE_TABLE_ENTRY entry;
+
+ /* Allocate a handle. */
+ status = KphpAllocateHandleEntry(HandleTable, &entry);
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ /* Reference and set the object in the entry. */
+ KphReferenceObject(Object);
+ entry->Object = Object;
+
+ /* Pass the handle back. */
+ *Handle = KphGetHandleEntry(entry);
+
+ return status;
+}
+
+/* KphReferenceObjectByHandle
+ *
+ * References an object from a handle.
+ */
+NTSTATUS KphReferenceObjectByHandle(
+ __in PKPH_HANDLE_TABLE HandleTable,
+ __in HANDLE Handle,
+ __in_opt PKPH_OBJECT_TYPE ObjectType,
+ __out PVOID *Object
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PKPH_HANDLE_TABLE_ENTRY entry;
+
+ if (!KphValidHandle(HandleTable, Handle, &entry))
+ return STATUS_INVALID_HANDLE;
+
+ /* Lock the entry. */
+ if (!KphLockAllocatedHandleEntry(entry))
+ return STATUS_INVALID_HANDLE;
+
+ /* Check the type of object if the caller requested us
+ * to do that.
+ */
+ if (ObjectType)
+ {
+ if (KphGetObjectType(entry->Object) != ObjectType)
+ {
+ /* Bad type. */
+ KphUnlockHandleEntry(entry);
+
+ return STATUS_OBJECT_TYPE_MISMATCH;
+ }
+ }
+
+ /* Reference and pass the object back. */
+ KphReferenceObject(entry->Object);
+ *Object = entry->Object;
+
+ KphUnlockHandleEntry(entry);
+
+ return status;
+}
+
+/* KphValidHandle
+ *
+ * Checks if a handle is valid.
+ */
+BOOLEAN KphValidHandle(
+ __in PKPH_HANDLE_TABLE HandleTable,
+ __in HANDLE Handle,
+ __out_opt PKPH_HANDLE_TABLE_ENTRY *Entry
+ )
+{
+ PKPH_HANDLE_TABLE_ENTRY entry;
+ BOOLEAN valid;
+
+ entry = KphEntryFromHandle(HandleTable, Handle);
+ valid =
+ ((ULONG_PTR)entry >= (ULONG_PTR)HandleTable->Table) &&
+ ((ULONG_PTR)entry + HandleTable->SizeOfEntry <=
+ (ULONG_PTR)HandleTable->Table + HandleTable->TableSize);
+
+ if (valid)
+ *Entry = entry;
+
+ return valid;
+}
+
+/* KphpAllocateHandleEntry
+ *
+ * Allocates a handle table entry.
+ */
+NTSTATUS KphpAllocateHandleEntry(
+ __in PKPH_HANDLE_TABLE HandleTable,
+ __out PKPH_HANDLE_TABLE_ENTRY *Entry
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PKPH_HANDLE_TABLE_ENTRY entry = NULL;
+
+ /* Prevent others from modifying the handle table. */
+ ExAcquireFastMutex(&HandleTable->Mutex);
+
+ /* Check the free list first. If we have a free entry,
+ * claim it and update the free list. Otherwise, create
+ * a new entry from the NextHandle value.
+ */
+ if (HandleTable->FreeHandle)
+ {
+ /* We have a free entry. Update the free list. */
+ entry = HandleTable->FreeHandle;
+ /* The next free entry goes into FreeHandle. */
+ HandleTable->FreeHandle = KphGetNextFreeEntry(entry);
+ }
+ else
+ {
+ /* No free handles. We have to initialize a new one
+ * based on the NextHandle value.
+ */
+ /* Make sure we don't go past the end of the table. */
+ if (
+ KphIndexFromHandle(HandleTable->NextHandle) *
+ HandleTable->SizeOfEntry <=
+ HandleTable->TableSize
+ )
+ {
+ /* Get a pointer to the entry from the handle. */
+ entry = KphEntryFromHandle(HandleTable, HandleTable->NextHandle);
+ /* Increment the next handle value. */
+ HandleTable->NextHandle = KphIncrementHandle(HandleTable->NextHandle);
+ }
+ else
+ {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+ }
+
+ if (NT_SUCCESS(status))
+ {
+ /* Set the entry's handle value. */
+ entry->Handle = KphHandleFromEntry(HandleTable, entry);
+ KphSetAllocatedEntry(entry);
+
+ *Entry = entry;
+ }
+
+ ExReleaseFastMutex(&HandleTable->Mutex);
+
+ return status;
+}
+
+/* KphpFreeHandleEntry
+ *
+ * Frees a handle table entry.
+ */
+NTSTATUS KphpFreeHandleEntry(
+ __in PKPH_HANDLE_TABLE HandleTable,
+ __in PKPH_HANDLE_TABLE_ENTRY Entry
+ )
+{
+ ExAcquireFastMutex(&HandleTable->Mutex);
+
+ /* Lock the entry. */
+ if (!KphLockAllocatedHandleEntry(Entry))
+ {
+ /* Someone else has already freed the entry (or it was never allocated). */
+ ExReleaseFastMutex(&HandleTable->Mutex);
+ return STATUS_INVALID_HANDLE;
+ }
+
+ /* Mark the entry as unallocated. */
+ KphClearAllocatedEntry(Entry);
+
+ /* Add the entry to the free list. */
+ KphSetNextFreeEntry(Entry, HandleTable->FreeHandle);
+ HandleTable->FreeHandle = Entry;
+
+ /* Zero the entry (except for the Value). */
+ memset(&Entry->Object, 0, HandleTable->SizeOfEntry - sizeof(ULONG_PTR));
+
+ /* Unlock the entry. */
+ KphUnlockHandleEntry(Entry);
+
+ ExReleaseFastMutex(&HandleTable->Mutex);
+
+ return STATUS_SUCCESS;
+}
diff --git a/branches/ph-plugins/KProcessHacker/hook.c b/branches/ph-plugins/KProcessHacker/hook.c
new file mode 100644
index 000000000..3f775db5d
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/hook.c
@@ -0,0 +1,408 @@
+/*
+ * Process Hacker Driver -
+ * hooks
+ *
+ * Copyright (C) 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 .
+ */
+
+#include "include/hook.h"
+#include "include/sync.h"
+
+static KPH_PROCESSOR_LOCK HookProcessorLock;
+
+/* KphHookInit
+ *
+ * Initializes the hooking module.
+ */
+NTSTATUS KphHookInit()
+{
+ KphInitializeProcessorLock(&HookProcessorLock);
+
+ return STATUS_SUCCESS;
+}
+
+/* KphInitializeHook
+ *
+ * Initializes a hook structure.
+ */
+VOID KphInitializeHook(
+ __out PKPH_HOOK Hook,
+ __in PVOID Function,
+ __in PVOID Target
+ )
+{
+ memset(Hook, 0, sizeof(KPH_HOOK));
+ Hook->Function = Function;
+ Hook->Target = Target;
+}
+
+/* KphHook
+ *
+ * Hooks a kernel-mode function.
+ * WARNING: DO NOT HOOK A FUNCTION THAT IS CALLABLE ABOVE APC_LEVEL.
+ *
+ * Thread safety: Full
+ * IRQL: <= APC_LEVEL
+ */
+NTSTATUS KphHook(
+ __inout PKPH_HOOK Hook
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ MAPPED_MDL mappedMdl;
+ PUCHAR function;
+
+ status = KphpCreateMappedMdl(
+ Hook->Function,
+ 5,
+ &mappedMdl
+ );
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ function = (PUCHAR)mappedMdl.Address;
+
+ /* Acquire a lock on all other processors. */
+ if (KphAcquireProcessorLock(&HookProcessorLock))
+ {
+ /* Note that this is completely safe even though we are at
+ * DISPATCH_LEVEL because we are using the mapped MDL.
+ */
+ /* Copy the original five bytes (for unhooking). */
+ memcpy(Hook->Bytes, function, 10);
+ /* Hook the function by writing a jump instruction. */
+ Hook->Hooked = TRUE;
+ /* jmp Target */
+ *function = 0xe9;
+ *(PULONG_PTR)(function + 1) = (ULONG_PTR)Hook->Target - (ULONG_PTR)Hook->Function - 5;
+
+ /* Release the processor lock. */
+ KphReleaseProcessorLock(&HookProcessorLock);
+ }
+ else
+ {
+ dprintf("KphHook: Could not acquire processor lock!\n");
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ KphpFreeMappedMdl(&mappedMdl);
+
+ return status;
+}
+
+/* KphUnhook
+ *
+ * Unhooks a kernel-mode function.
+ * WARNING: DO NOT UNHOOK A FUNCTION THAT IS CALLABLE ABOVE APC_LEVEL.
+ *
+ * Thread safety: Full
+ * IRQL: <= APC_LEVEL
+ */
+NTSTATUS KphUnhook(
+ __inout PKPH_HOOK Hook
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ MAPPED_MDL mappedMdl;
+
+ if (!Hook->Hooked)
+ return STATUS_UNSUCCESSFUL;
+
+ status = KphpCreateMappedMdl(
+ Hook->Function,
+ 5,
+ &mappedMdl
+ );
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ /* Acquire a lock on all other processors. */
+ if (KphAcquireProcessorLock(&HookProcessorLock))
+ {
+ /* Unpatch the function. */
+ memcpy(mappedMdl.Address, Hook->Bytes, 5);
+ Hook->Hooked = FALSE;
+ /* Release the processor lock. */
+ KphReleaseProcessorLock(&HookProcessorLock);
+ }
+ else
+ {
+ dprintf("KphUnhook: Could not acquire processor lock!\n");
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ KphpFreeMappedMdl(&mappedMdl);
+
+ return status;
+}
+
+/* KphObOpenCall
+ *
+ * Calls the original open procedure for an object type.
+ *
+ * AccessMode: If this argument is unavailable, specify KernelMode.
+ */
+NTSTATUS NTAPI KphObOpenCall(
+ __in PKPH_OB_OPEN_HOOK ObOpenHook,
+ __in OB_OPEN_REASON OpenReason,
+ __in KPROCESSOR_MODE AccessMode,
+ __in PEPROCESS Process,
+ __in PVOID Object,
+ __in ACCESS_MASK GrantedAccess,
+ __in ULONG HandleCount
+ )
+{
+ /* If there wasn't any original open procedure, exit. */
+ if (!ObOpenHook->Function)
+ return STATUS_SUCCESS;
+
+ if (WindowsVersion == WINDOWS_XP)
+ {
+ return ((OB_OPEN_METHOD_51)ObOpenHook->Function)(
+ OpenReason,
+ Process,
+ Object,
+ GrantedAccess,
+ HandleCount
+ );
+ }
+ else if (
+ WindowsVersion == WINDOWS_VISTA ||
+ WindowsVersion == WINDOWS_7
+ )
+ {
+ return ((OB_OPEN_METHOD_60)ObOpenHook->Function)(
+ OpenReason,
+ AccessMode,
+ Process,
+ Object,
+ GrantedAccess,
+ HandleCount
+ );
+ }
+ else
+ {
+ return STATUS_NOT_SUPPORTED;
+ }
+}
+
+/* KphInitializeObOpenHook
+ *
+ * Initializes a hook structure.
+ */
+VOID KphInitializeObOpenHook(
+ __inout PKPH_OB_OPEN_HOOK ObOpenHook,
+ __in POBJECT_TYPE ObjectType,
+ __in PVOID Target51,
+ __in PVOID Target60
+ )
+{
+ memset(ObOpenHook, 0, sizeof(KPH_OB_OPEN_HOOK));
+ ObOpenHook->ObjectType = ObjectType;
+ ObOpenHook->Target51 = Target51;
+ ObOpenHook->Target60 = Target60;
+}
+
+/* KphObOpenHook
+ *
+ * Hooks the open procedure for an object type.
+ *
+ * Thread safety: Full
+ * IRQL: <= APC_LEVEL
+ */
+NTSTATUS KphObOpenHook(
+ __inout PKPH_OB_OPEN_HOOK ObOpenHook
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ MAPPED_MDL mappedMdl;
+ PVOID *openProcedure;
+
+ status = KphpCreateMappedMdl(
+ KVOFF(ObOpenHook->ObjectType, OffOtiOpenProcedure),
+ sizeof(PVOID),
+ &mappedMdl
+ );
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ openProcedure = (PVOID *)mappedMdl.Address;
+
+ /* Acquire a lock on all other processors. */
+ if (KphAcquireProcessorLock(&HookProcessorLock))
+ {
+ /* Save the original open procedure pointer. */
+ ObOpenHook->Function = *openProcedure;
+
+ /* Choose the correct target open procedure and hook. */
+ if (WindowsVersion == WINDOWS_XP)
+ {
+ if (ObOpenHook->Target51)
+ *openProcedure = ObOpenHook->Target51;
+ else
+ status = STATUS_INVALID_PARAMETER;
+ }
+ else if (
+ WindowsVersion == WINDOWS_VISTA ||
+ WindowsVersion == WINDOWS_7
+ )
+ {
+ if (ObOpenHook->Target60)
+ *openProcedure = ObOpenHook->Target60;
+ else
+ status = STATUS_INVALID_PARAMETER;
+ }
+ else
+ {
+ status = STATUS_NOT_SUPPORTED;
+ }
+
+ ObOpenHook->Hooked = TRUE;
+
+ /* Release the processor lock. */
+ KphReleaseProcessorLock(&HookProcessorLock);
+ }
+ else
+ {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ KphpFreeMappedMdl(&mappedMdl);
+
+ return status;
+}
+
+/* KphObOpenUnhook
+ *
+ * Unhooks the open procedure for an object type.
+ *
+ * Thread safety: Full
+ * IRQL: <= APC_LEVEL
+ */
+NTSTATUS KphObOpenUnhook(
+ __inout PKPH_OB_OPEN_HOOK ObOpenHook
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ MAPPED_MDL mappedMdl;
+ PVOID *openProcedure;
+
+ if (!ObOpenHook->Hooked)
+ return STATUS_UNSUCCESSFUL;
+
+ status = KphpCreateMappedMdl(
+ KVOFF(ObOpenHook->ObjectType, OffOtiOpenProcedure),
+ sizeof(PVOID),
+ &mappedMdl
+ );
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ openProcedure = (PVOID *)mappedMdl.Address;
+
+ /* Acquire a lock on all other processors. */
+ if (KphAcquireProcessorLock(&HookProcessorLock))
+ {
+ /* Restore the original open procedure pointer. */
+ *openProcedure = ObOpenHook->Function;
+ ObOpenHook->Hooked = FALSE;
+
+ /* Release the processor lock. */
+ KphReleaseProcessorLock(&HookProcessorLock);
+ }
+ else
+ {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ KphpFreeMappedMdl(&mappedMdl);
+
+ return status;
+}
+
+/* KphpCreateMappedMdl
+ *
+ * Creates and maps a MDL.
+ *
+ * Thread safety: Full
+ * IRQL: Any
+ */
+NTSTATUS KphpCreateMappedMdl(
+ __in PVOID Address,
+ __in ULONG Length,
+ __out PMAPPED_MDL MappedMdl
+ )
+{
+ PMDL mdl;
+
+ MappedMdl->Mdl = NULL;
+ MappedMdl->Address = NULL;
+
+ mdl = IoAllocateMdl(Address, Length, FALSE, FALSE, NULL);
+
+ if (mdl == NULL)
+ return STATUS_INSUFFICIENT_RESOURCES;
+
+ MmBuildMdlForNonPagedPool(mdl);
+ mdl->MdlFlags |= MDL_MAPPED_TO_SYSTEM_VA;
+ MappedMdl->Address = MmMapLockedPagesSpecifyCache(
+ mdl,
+ KernelMode,
+ MmNonCached,
+ NULL,
+ FALSE,
+ HighPagePriority
+ );
+ MappedMdl->Mdl = mdl;
+
+ if (!MappedMdl->Address)
+ {
+ KphpFreeMappedMdl(MappedMdl);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+/* KphpFreeMappedMdl
+ *
+ * Unmaps and frees a MDL.
+ *
+ * Thread safety: Full
+ * IRQL: Any
+ */
+VOID KphpFreeMappedMdl(
+ __in PMAPPED_MDL MappedMdl
+ )
+{
+ if (MappedMdl->Mdl != NULL)
+ {
+ if (MappedMdl->Address != NULL)
+ {
+ MmUnmapLockedPages(MappedMdl->Address, MappedMdl->Mdl);
+ MappedMdl->Address = NULL;
+ }
+
+ IoFreeMdl(MappedMdl->Mdl);
+ MappedMdl->Mdl = NULL;
+ }
+}
diff --git a/branches/ph-plugins/KProcessHacker/i386/kprocesshacker.sys b/branches/ph-plugins/KProcessHacker/i386/kprocesshacker.sys
new file mode 100644
index 000000000..ae87f7343
Binary files /dev/null and b/branches/ph-plugins/KProcessHacker/i386/kprocesshacker.sys differ
diff --git a/branches/ph-plugins/KProcessHacker/include/debug.h b/branches/ph-plugins/KProcessHacker/include/debug.h
new file mode 100644
index 000000000..0a140616b
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/include/debug.h
@@ -0,0 +1,35 @@
+/*
+ * Process Hacker Driver -
+ * debug definitions
+ *
+ * Copyright (C) 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 .
+ */
+
+#ifndef _DEBUG_H
+#define _DEBUG_H
+
+#ifdef DBG
+#define dprintf(fs, ...) DbgPrint("KProcessHacker: " fs, __VA_ARGS__)
+#else
+#define dprintf
+#endif
+
+#define dfprintf(fs, ...) DbgPrint("KProcessHacker: " fs, __VA_ARGS__)
+#define dwprintf DbgPrint
+
+#endif
diff --git a/branches/ph-plugins/KProcessHacker/include/ex.h b/branches/ph-plugins/KProcessHacker/include/ex.h
new file mode 100644
index 000000000..bfd06ccbe
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/include/ex.h
@@ -0,0 +1,262 @@
+/*
+ * Process Hacker Driver -
+ * executive
+ *
+ * Copyright (C) 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 .
+ */
+
+#ifndef _EX_H
+#define _EX_H
+
+#include "types.h"
+
+/* HACK - version.c dependency */
+#define WINDOWS_XP 51
+#define WINDOWS_SERVER_2003 52
+#define WINDOWS_VISTA 60
+#define WINDOWS_7 61
+
+extern ULONG WindowsVersion;
+
+/* Handles */
+
+struct _HANDLE_TABLE;
+struct _HANDLE_TABLE_ENTRY;
+
+typedef BOOLEAN (NTAPI *PEX_ENUM_HANDLE_CALLBACK)(
+ struct _HANDLE_TABLE_ENTRY *HandleTableEntry,
+ HANDLE Handle,
+ PVOID Context
+ );
+
+BOOLEAN NTAPI ExEnumHandleTable(
+ __in struct _HANDLE_TABLE *HandleTable,
+ __in PEX_ENUM_HANDLE_CALLBACK EnumHandleProcedure,
+ __inout PVOID Context,
+ __out_opt PHANDLE Handle
+ );
+
+/* Push Locks */
+
+/* Definition for Windows 2003 and above. This means we
+ * MUST use the slow path on Windows XP.
+ */
+typedef struct _EXI_PUSH_LOCK
+{
+ union
+ {
+ struct
+ {
+ ULONG_PTR Locked : 1;
+ ULONG_PTR Waiting : 1;
+ ULONG_PTR Waking : 1;
+ ULONG_PTR MultipleShared : 1;
+ ULONG_PTR Shared : sizeof(ULONG_PTR) * 8 - 4; /* ULONG_PTR bits minus 4 */
+ };
+ ULONG_PTR Value;
+ PVOID Ptr;
+ };
+} EXI_PUSH_LOCK, *PEXI_PUSH_LOCK;
+
+#define EX_PUSH_LOCK_LOCK_SHIFT 0
+#define EX_PUSH_LOCK_LOCK ((ULONG_PTR)0x1)
+/* Indicates chained waiters */
+#define EX_PUSH_LOCK_WAITING ((ULONG_PTR)0x2)
+/* Traversing the list */
+#define EX_PUSH_LOCK_WAKING ((ULONG_PTR)0x4)
+/* Multiple owners + waiters */
+#define EX_PUSH_LOCK_MULTIPLE_SHARED ((ULONG_PTR)0x8)
+
+#define EX_PUSH_LOCK_SHARE_INC ((ULONG_PTR)0x10)
+#define EX_PUSH_LOCK_PTR_BITS ((ULONG_PTR)0xf)
+
+NTKERNELAPI VOID FASTCALL ExfAcquirePushLockExclusive(
+ __inout PEX_PUSH_LOCK PushLock
+ );
+
+NTKERNELAPI VOID FASTCALL ExfAcquirePushLockShared(
+ __inout PEX_PUSH_LOCK PushLock
+ );
+
+NTKERNELAPI VOID FASTCALL ExfReleasePushLock(
+ __inout PEX_PUSH_LOCK PushLock
+ );
+
+/* The below functions are only exported on Vista and higher. */
+
+NTKERNELAPI VOID FASTCALL ExfReleasePushLockShared(
+ __inout PEX_PUSH_LOCK PushLock
+ );
+
+NTKERNELAPI VOID FASTCALL ExfReleasePushLockExclusive(
+ __inout PEX_PUSH_LOCK PushLock
+ );
+
+NTKERNELAPI BOOLEAN FASTCALL ExfTryAcquirePushLockShared(
+ __inout PEX_PUSH_LOCK PushLock
+ );
+
+NTKERNELAPI VOID FASTCALL ExfTryToWakePushLock(
+ __inout PEX_PUSH_LOCK PushLock
+ );
+
+/* Wrapper functions */
+
+/* ExInitializePushLock
+ *
+ * Initializes a push lock.
+ */
+FORCEINLINE VOID ExInitializePushLock(
+ __out PEX_PUSH_LOCK PushLock
+ )
+{
+ *PushLock = 0;
+}
+
+/* ExAcquirePushLockExclusive
+ *
+ * Acquires a push lock in exclusive mode.
+ */
+FORCEINLINE VOID ExAcquirePushLockExclusive(
+ __inout PEX_PUSH_LOCK PushLock
+ )
+{
+ /* Fast path - acquire push lock, no function call. */
+ if (WindowsVersion < WINDOWS_SERVER_2003 || InterlockedBitTestAndSet((PLONG)PushLock, EX_PUSH_LOCK_LOCK_SHIFT))
+ {
+ /* Slow path - call the function. */
+ ExfAcquirePushLockExclusive(PushLock);
+ }
+}
+
+/* ExAcquirePushLockShared
+ *
+ * Acquires a push lock in shared mode.
+ */
+FORCEINLINE VOID ExAcquirePushLockShared(
+ __inout PEX_PUSH_LOCK PushLock
+ )
+{
+ /* Fast path - acquire push lock which is not held at all, no function call. */
+ if (WindowsVersion < WINDOWS_SERVER_2003 || InterlockedCompareExchangePointer(
+ (PVOID)PushLock,
+ (PVOID)(EX_PUSH_LOCK_SHARE_INC | EX_PUSH_LOCK_LOCK),
+ 0
+ ) != 0)
+ {
+ /* Slow path - call the function. */
+ ExfAcquirePushLockShared(PushLock);
+ }
+}
+
+/* ExReleasePushLock
+ *
+ * Releases a push lock (for both types).
+ */
+FORCEINLINE VOID ExReleasePushLock(
+ __inout PEX_PUSH_LOCK PushLock
+ )
+{
+ EXI_PUSH_LOCK oldValue, newValue;
+
+ oldValue.Value = *PushLock;
+
+ /* If we are the last to release in shared mode or we
+ * are releasing in exclusive mode, we simply set
+ * the value to 0.
+ */
+
+ if (oldValue.Shared > 1)
+ {
+ /* One less shared holder. */
+ newValue.Value = oldValue.Value - EX_PUSH_LOCK_SHARE_INC;
+ }
+ else
+ {
+ newValue.Value = 0;
+ }
+
+ /* If we have chained waiters, we can't release the
+ * push lock using the fast path since they need to
+ * be unblocked.
+ */
+ if (
+ WindowsVersion < WINDOWS_SERVER_2003 ||
+ oldValue.Waiting ||
+ InterlockedCompareExchangePointer(
+ (PVOID)PushLock,
+ newValue.Ptr,
+ oldValue.Ptr
+ ) != oldValue.Ptr
+ )
+ {
+ /* Slow path - call the function. */
+ ExfReleasePushLock(PushLock);
+ }
+}
+
+#ifndef NEVER_DEFINED
+/* ExTryAcquirePushLockExclusive
+ *
+ * Attempts to acquire a push lock in exclusive mode.
+ *
+ * Return value: TRUE if the push lock was acquired, FALSE if
+ * the push lock was already acquired in exclusive mode.
+ */
+FORCEINLINE BOOLEAN ExTryAcquirePushLockExclusive(
+ __inout PEX_PUSH_LOCK PushLock
+ )
+{
+ if (!InterlockedBitTestAndSet((PLONG)PushLock, EX_PUSH_LOCK_LOCK_SHIFT))
+ {
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+/* ExTryAcquirePushLockShared
+ *
+ * Attempts to acquire a push lock in shared mode.
+ *
+ * Return value: TRUE if the push lock was acquired, FALSE if
+ * the push lock was already acquired in exclusive mode.
+ */
+FORCEINLINE BOOLEAN ExTryAcquirePushLockShared(
+ __inout PEX_PUSH_LOCK PushLock
+ )
+{
+ /* Fast path with the push lock not held at all. */
+ if (InterlockedCompareExchangePointer(
+ (PVOID)PushLock,
+ (PVOID)(EX_PUSH_LOCK_SHARE_INC | EX_PUSH_LOCK_LOCK),
+ 0
+ ) != 0)
+ {
+ return ExfTryAcquirePushLockShared(PushLock);
+ }
+ else
+ {
+ return TRUE;
+ }
+}
+#endif
+
+#endif
\ No newline at end of file
diff --git a/branches/ph-plugins/KProcessHacker/include/handle.h b/branches/ph-plugins/KProcessHacker/include/handle.h
new file mode 100644
index 000000000..710205b3e
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/include/handle.h
@@ -0,0 +1,78 @@
+/*
+ * Process Hacker Driver -
+ * handle table
+ *
+ * Copyright (C) 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 .
+ */
+
+#ifndef _HANDLE_H
+#define _HANDLE_H
+
+#include "kph.h"
+#include "ref.h"
+
+struct _KPH_HANDLE_TABLE;
+typedef struct _KPH_HANDLE_TABLE *PKPH_HANDLE_TABLE;
+
+typedef struct _KPH_HANDLE_TABLE_ENTRY
+{
+ union
+ {
+ HANDLE Handle;
+ ULONG_PTR Value;
+ struct _KPH_HANDLE_TABLE_ENTRY *NextFree;
+ };
+ PVOID Object;
+} KPH_HANDLE_TABLE_ENTRY, *PKPH_HANDLE_TABLE_ENTRY;
+
+NTSTATUS KphCreateHandleTable(
+ __out PKPH_HANDLE_TABLE *HandleTable,
+ __in ULONG MaximumHandles,
+ __in ULONG SizeOfEntry,
+ __in ULONG Tag
+ );
+
+VOID KphFreeHandleTable(
+ __in PKPH_HANDLE_TABLE HandleTable
+ );
+
+NTSTATUS KphCloseHandle(
+ __in PKPH_HANDLE_TABLE HandleTable,
+ __in HANDLE Handle
+ );
+
+NTSTATUS KphCreateHandle(
+ __in PKPH_HANDLE_TABLE HandleTable,
+ __in PVOID Object,
+ __out PHANDLE Handle
+ );
+
+NTSTATUS KphReferenceObjectByHandle(
+ __in PKPH_HANDLE_TABLE HandleTable,
+ __in HANDLE Handle,
+ __in_opt PKPH_OBJECT_TYPE ObjectType,
+ __out PVOID *Object
+ );
+
+BOOLEAN KphValidHandle(
+ __in PKPH_HANDLE_TABLE HandleTable,
+ __in HANDLE Handle,
+ __out_opt PKPH_HANDLE_TABLE_ENTRY *Entry
+ );
+
+#endif
diff --git a/branches/ph-plugins/KProcessHacker/include/handlep.h b/branches/ph-plugins/KProcessHacker/include/handlep.h
new file mode 100644
index 000000000..a6cd445ee
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/include/handlep.h
@@ -0,0 +1,144 @@
+/*
+ * Process Hacker Driver -
+ * handle table
+ *
+ * Copyright (C) 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 .
+ */
+
+#ifndef _HANDLEP_H
+#define _HANDLEP_H
+
+#define _HANDLE_PRIVATE
+#include "handle.h"
+#include "sync.h"
+
+#define KPH_HANDLE_INCREMENT 4
+#define KPH_HANDLE_LOCKED 0x1
+#define KPH_HANDLE_LOCKED_SHIFT 0
+#define KPH_HANDLE_ALLOCATED 0x2
+#define KPH_HANDLE_FLAGS 0x3
+
+#define KphGetFlagsEntry(Entry) ((Entry)->Value & KPH_HANDLE_FLAGS)
+#define KphGetHandleEntry(Entry) ((HANDLE)((Entry)->Value & ~KPH_HANDLE_FLAGS))
+#define KphIncrementHandle(Handle) ((HANDLE)((ULONG_PTR)(Handle) + KPH_HANDLE_INCREMENT))
+
+#define KphIsAllocatedEntry(Entry) ((Entry)->Value & KPH_HANDLE_ALLOCATED)
+#define KphClearAllocatedEntry(Entry) ((Entry)->Value &= ~KPH_HANDLE_ALLOCATED)
+#define KphSetAllocatedEntry(Entry) ((Entry)->Value |= KPH_HANDLE_ALLOCATED)
+
+#define KphGetNextFreeEntry(Entry) ((PKPH_HANDLE_TABLE_ENTRY)((Entry)->Value & ~KPH_HANDLE_FLAGS))
+#define KphSetNextFreeEntry(Entry, NextFree) ((Entry)->Value = ((ULONG_PTR)(NextFree) | KphGetFlagsEntry(Entry)))
+
+#define KphHandleFromIndex(Index) ((HANDLE)((ULONG_PTR)(Index) * KPH_HANDLE_INCREMENT))
+#define KphHandleFromIndexEx(Index, Flags) ((HANDLE)(((Index) * KPH_HANDLE_INCREMENT) | (Flags)))
+#define KphIndexFromHandle(Handle) (((ULONG_PTR)(Handle) & ~KPH_HANDLE_FLAGS) / KPH_HANDLE_INCREMENT)
+
+#define KphEntryFromHandle(HandleTable, Handle) KphEntryFromIndex((HandleTable), KphIndexFromHandle(Handle))
+#define KphEntryFromIndex(HandleTable, Index) \
+ ((PKPH_HANDLE_TABLE_ENTRY)((ULONG_PTR)(HandleTable)->Table + (Index) * (HandleTable)->SizeOfEntry))
+#define KphHandleFromEntry(HandleTable, Entry) KphHandleFromIndex(KphIndexFromEntry((HandleTable), (Entry)))
+#define KphHandleFromEntryEx(HandleTable, Entry, Flags) \
+ KphHandleFromIndexEx(KphIndexFromEntry((HandleTable), (Entry)), (Flags))
+#define KphIndexFromEntry(HandleTable, Entry) \
+ (((ULONG_PTR)(Entry) - (ULONG_PTR)(HandleTable)->Table) / (HandleTable)->SizeOfEntry)
+
+typedef struct _KPH_HANDLE_TABLE
+{
+ /* The pool tag used for this descriptor and the table itself. */
+ ULONG Tag;
+ /* The size of each handle table entry. */
+ ULONG SizeOfEntry;
+ /* The next handle value to use. */
+ HANDLE NextHandle;
+ /* The free list of handle table entries. */
+ struct _KPH_HANDLE_TABLE_ENTRY *FreeHandle;
+
+ /* A fast mutex guarding writes to the handle table. */
+ FAST_MUTEX Mutex;
+ /* The size of the table, in bytes. */
+ ULONG TableSize;
+ /* The actual handle table. */
+ PVOID Table;
+} KPH_HANDLE_TABLE, *PKPH_HANDLE_TABLE;
+
+FORCEINLINE BOOLEAN KphLockHandleEntry(
+ __inout PKPH_HANDLE_TABLE_ENTRY Entry
+ );
+
+FORCEINLINE BOOLEAN KphLockAllocatedHandleEntry(
+ __inout PKPH_HANDLE_TABLE_ENTRY Entry
+ );
+
+FORCEINLINE VOID KphUnlockHandleEntry(
+ __inout PKPH_HANDLE_TABLE_ENTRY Entry
+ );
+
+/* KphLockHandle
+ *
+ * Locks a handle table entry for exclusive access. Do not
+ * modify the lowest bit of the entry's value while you
+ * hold the lock.
+ *
+ * Return value: TRUE if the entry is allocated, otherwise FALSE.
+ */
+FORCEINLINE BOOLEAN KphLockHandleEntry(
+ __inout PKPH_HANDLE_TABLE_ENTRY Entry
+ )
+{
+ /* Acquire the spinlock. */
+ KphAcquireBitSpinLock((PLONG)&Entry->Value, KPH_HANDLE_LOCKED_SHIFT);
+
+ /* Return whether the entry is allocated. */
+ return !!(Entry->Value & KPH_HANDLE_ALLOCATED);
+}
+
+/* KphLockAllocatedHandle
+ *
+ * Locks a handle table entry for exclusive access. Do not
+ * modify the lowest bit of the entry's value while you
+ * hold the lock.
+ * The function will not lock the handle if it is unallocated.
+ *
+ * Return value: TRUE if the entry was locked, otherwise FALSE.
+ */
+FORCEINLINE BOOLEAN KphLockAllocatedHandleEntry(
+ __inout PKPH_HANDLE_TABLE_ENTRY Entry
+ )
+{
+ if (!KphLockHandleEntry(Entry))
+ {
+ KphUnlockHandleEntry(Entry);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* KphUnlockHandle
+ *
+ * Unlocks a handle table entry.
+ */
+FORCEINLINE VOID KphUnlockHandleEntry(
+ __inout PKPH_HANDLE_TABLE_ENTRY Entry
+ )
+{
+ /* Unlock the spinlock. */
+ KphReleaseBitSpinLock((PLONG)&Entry->Value, KPH_HANDLE_LOCKED_SHIFT);
+}
+
+#endif
diff --git a/branches/ph-plugins/KProcessHacker/include/hook.h b/branches/ph-plugins/KProcessHacker/include/hook.h
new file mode 100644
index 000000000..12ec3d3f9
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/include/hook.h
@@ -0,0 +1,108 @@
+/*
+ * Process Hacker Driver -
+ * hooks
+ *
+ * Copyright (C) 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 .
+ */
+
+#ifndef _HOOK_H
+#define _HOOK_H
+
+#include "kph.h"
+#include "ob.h"
+
+#define KPH_DEFINE_HOOK_CALL(Name, Arguments, Hook) \
+ __declspec(naked) Name(Arguments) \
+ { \
+ __asm lea eax, Hook \
+ __asm mov eax, [eax+KPH_HOOK.Function] \
+ __asm add eax, 5 \
+ __asm push ebp \
+ __asm mov ebp, esp \
+ __asm jmp eax \
+ } \
+
+typedef struct _KPH_HOOK
+{
+ /* The address of the hooked function.
+ Should NOT be a function that is callable above PASSIVE_LEVEL. */
+ PVOID Function;
+ /* The address of the new function. */
+ PVOID Target;
+ /* Whether the function is hooked. */
+ BOOLEAN Hooked;
+ /* The original first 10 bytes. */
+ CHAR Bytes[10];
+} KPH_HOOK, *PKPH_HOOK;
+
+typedef struct _KPH_OB_OPEN_HOOK
+{
+ /* The object type that is being hooked. */
+ POBJECT_TYPE ObjectType;
+ /* The original open procedure. */
+ PVOID Function;
+ /* The new open procedure for NT 5.1 (XP). */
+ OB_OPEN_METHOD_51 Target51;
+ /* The new open procedure for NT 6.1 and above (Vista, 7 or higher). */
+ OB_OPEN_METHOD_60 Target60;
+ /* Whether the open procedure is hooked. */
+ BOOLEAN Hooked;
+} KPH_OB_OPEN_HOOK, *PKPH_OB_OPEN_HOOK;
+
+NTSTATUS KphHookInit();
+
+VOID KphInitializeHook(
+ __out PKPH_HOOK Hook,
+ __in PVOID Function,
+ __in PVOID Target
+ );
+
+NTSTATUS KphHook(
+ __inout PKPH_HOOK Hook
+ );
+
+NTSTATUS KphUnhook(
+ __inout PKPH_HOOK Hook
+ );
+
+NTSTATUS NTAPI KphObOpenCall(
+ __in PKPH_OB_OPEN_HOOK ObOpenHook,
+ __in OB_OPEN_REASON OpenReason,
+ __in KPROCESSOR_MODE AccessMode,
+ __in PEPROCESS Process,
+ __in PVOID Object,
+ __in ACCESS_MASK GrantedAccess,
+ __in ULONG HandleCount
+ );
+
+VOID KphInitializeObOpenHook(
+ __inout PKPH_OB_OPEN_HOOK ObOpenHook,
+ __in POBJECT_TYPE ObjectType,
+ __in PVOID Target51,
+ __in PVOID Target60
+ );
+
+NTSTATUS KphObOpenHook(
+ __inout PKPH_OB_OPEN_HOOK ObOpenHook
+ );
+
+NTSTATUS KphObOpenUnhook(
+ __inout PKPH_OB_OPEN_HOOK ObOpenHook
+ );
+
+#endif
diff --git a/branches/ph-plugins/KProcessHacker/include/io.h b/branches/ph-plugins/KProcessHacker/include/io.h
new file mode 100644
index 000000000..bc98fb151
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/include/io.h
@@ -0,0 +1,34 @@
+/*
+ * Process Hacker Driver -
+ * I/O manager
+ *
+ * Copyright (C) 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 .
+ */
+
+#ifndef _IO_H
+#define _IO_H
+
+#include "types.h"
+
+extern POBJECT_TYPE *IoAdapterObjectType;
+extern POBJECT_TYPE *IoControllerObjectType;
+extern POBJECT_TYPE *IoDeviceHandlerObjectType; /* not used anymore */
+extern POBJECT_TYPE *IoDeviceObjectType;
+extern POBJECT_TYPE *IoDriverObjectType;
+
+#endif
\ No newline at end of file
diff --git a/branches/ph-plugins/KProcessHacker/include/ke.h b/branches/ph-plugins/KProcessHacker/include/ke.h
new file mode 100644
index 000000000..741c1b00b
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/include/ke.h
@@ -0,0 +1,95 @@
+/*
+ * Process Hacker Driver -
+ * kernel
+ *
+ * Copyright (C) 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 .
+ */
+
+#ifndef _KE_H
+#define _KE_H
+
+#include "types.h"
+
+/* APCs */
+
+typedef enum _KAPC_ENVIRONMENT
+{
+ OriginalApcEnvironment,
+ AttachedApcEnvironment,
+ CurrentApcEnvironment,
+ InsertApcEnvironment
+} KAPC_ENVIRONMENT, *PKAPC_ENVIRONMENT;
+
+typedef VOID (NTAPI *PKKERNEL_ROUTINE)(
+ PKAPC Apc,
+ PKNORMAL_ROUTINE *NormalRoutine,
+ PVOID *NormalContext,
+ PVOID *SystemArgument1,
+ PVOID *SystemArgument2
+ );
+
+typedef VOID (NTAPI *PKRUNDOWN_ROUTINE)(
+ PKAPC Apc
+ );
+
+typedef VOID (NTAPI *PKNORMAL_ROUTINE)(
+ PVOID NormalContext,
+ PVOID SystemArgument1,
+ PVOID SystemArgument2
+ );
+
+NTKERNELAPI VOID NTAPI KeInitializeApc(
+ PKAPC Apc,
+ PKTHREAD Thread,
+ KAPC_ENVIRONMENT Environment,
+ PKKERNEL_ROUTINE KernelRoutine,
+ PKRUNDOWN_ROUTINE RundownRoutine,
+ PKNORMAL_ROUTINE NormalRoutine,
+ KPROCESSOR_MODE ProcessorMode,
+ PVOID NormalContext
+ );
+
+NTKERNELAPI BOOLEAN NTAPI KeInsertQueueApc(
+ PRKAPC Apc,
+ PVOID SystemArgument1,
+ PVOID SystemArgument2,
+ KPRIORITY Increment
+ );
+
+/* System services */
+
+/* Exported by ntoskrnl as KeServiceDescriptorTable. */
+typedef struct _KSERVICE_TABLE_DESCRIPTOR
+{
+ /* A pointer to an array of ULONG_PTRs - addresses of
+ * system services.
+ */
+ PULONG_PTR Base;
+ /* A pointer to an array of ULONGs which contain counters for
+ * the system services.
+ */
+ PULONG Count;
+ /* The number of system services. */
+ ULONG Limit;
+ /* A pointer to an array of UCHARs which contain
+ * the number of arguments (in bytes) for each system service.
+ */
+ PUCHAR Number;
+} KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR;
+
+#endif
\ No newline at end of file
diff --git a/branches/ph-plugins/KProcessHacker/include/kph.h b/branches/ph-plugins/KProcessHacker/include/kph.h
new file mode 100644
index 000000000..91e0b3db4
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/include/kph.h
@@ -0,0 +1,503 @@
+/*
+ * Process Hacker Driver -
+ * custom APIs
+ *
+ * Copyright (C) 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 .
+ */
+
+#ifndef _KPH_H
+#define _KPH_H
+
+#include "types.h"
+#include "debug.h"
+#include "ref.h"
+#include "version.h"
+
+#include "ke.h"
+#include "mm.h"
+#include "ps.h"
+#include "trace.h"
+#include "zw.h"
+
+#define MAX_UINTEGER(Bits) ((1 << (Bits)) - 1)
+#define BITS_UCHAR 8
+#define MAX_UCHAR MAX_UINTEGER(BITS_UCHAR)
+#define BITS_USHORT 16
+#define MAX_USHORT MAX_UINTEGER(BITS_USHORT)
+#define BITS_ULONG 32
+#define MAX_ULONG MAX_UINTEGER(BITS_ULONG)
+
+#define SYSTEM_PROCESS_ID ((HANDLE)4)
+#define KERNEL_HANDLE_BIT ((ULONG_PTR)1 << (sizeof(HANDLE) * 8 - 1))
+#define IsKernelHandle(Handle) ((LONG_PTR)(Handle) < 0)
+#define MakeKernelHandle(Handle) ((ULONG_PTR)(Handle) |= KERNEL_HANDLE_BIT)
+
+#define PTR_ADD_OFFSET(Pointer, Offset) ((PVOID)((ULONG_PTR)(Pointer) + (ULONG_PTR)(Offset)))
+
+#define GET_BIT(Integer, Bit) (((Integer) >> (Bit)) & 0x1)
+#define SET_BIT(Integer, Bit) ((Integer) |= 1 << (Bit))
+#define CLEAR_BIT(Integer, Bit) ((Integer) &= ~(1 << (Bit)))
+
+#define KPH_TIMEOUT_TO_SEC ((LONGLONG) 1 * 10 * 1000 * 1000)
+#define KPH_REL_TIMEOUT_IN_SEC(Time) (Time * -1 * KPH_TIMEOUT_TO_SEC)
+
+#define TAG_CAPTURED_UNICODE_STRING ('UChP')
+
+#ifdef EXT
+#undef EXT
+#endif
+
+#ifdef _KPH_PRIVATE
+#define EXT
+#define EQNULL = NULL
+#else
+#define EXT extern
+#define EQNULL
+#endif
+
+EXT PKSERVICE_TABLE_DESCRIPTOR __KeServiceDescriptorTable EQNULL;
+EXT PVOID __KiFastCallEntry EQNULL;
+EXT _NtClose __NtClose EQNULL;
+EXT _ObGetObjectType ObGetObjectType EQNULL;
+EXT _PsGetProcessJob PsGetProcessJob EQNULL;
+EXT _PsResumeProcess PsResumeProcess EQNULL;
+EXT _PsSuspendProcess PsSuspendProcess EQNULL;
+EXT _PsTerminateProcess __PsTerminateProcess EQNULL;
+EXT PVOID __PspTerminateThreadByPointer EQNULL;
+EXT _NtClose __ZwClose EQNULL;
+
+/* Driver information */
+
+typedef enum _DRIVER_INFORMATION_CLASS
+{
+ DriverBasicInformation,
+ DriverNameInformation,
+ DriverServiceKeyNameInformation,
+ MaxDriverInfoClass
+} DRIVER_INFORMATION_CLASS;
+
+typedef struct _DRIVER_BASIC_INFORMATION
+{
+ ULONG Flags;
+ PVOID DriverStart;
+ ULONG DriverSize;
+} DRIVER_BASIC_INFORMATION, *PDRIVER_BASIC_INFORMATION;
+
+typedef struct _KPH_ATTACH_STATE
+{
+ BOOLEAN Attached;
+ PEPROCESS Process;
+ KAPC_STATE ApcState;
+} KPH_ATTACH_STATE, *PKPH_ATTACH_STATE;
+
+typedef struct _MAPPED_MDL
+{
+ PMDL Mdl;
+ PVOID Address;
+} MAPPED_MDL, *PMAPPED_MDL;
+
+typedef struct _PROCESS_HANDLE
+{
+ HANDLE Handle;
+ PVOID Object;
+ ACCESS_MASK GrantedAccess;
+ ULONG HandleAttributes;
+} PROCESS_HANDLE, *PPROCESS_HANDLE;
+
+typedef struct _PROCESS_HANDLE_INFORMATION
+{
+ ULONG HandleCount;
+ PROCESS_HANDLE Handles[1];
+} PROCESS_HANDLE_INFORMATION, *PPROCESS_HANDLE_INFORMATION;
+
+/* Support routines */
+
+NTSTATUS KphNtInit();
+
+PVOID GetSystemRoutineAddress(
+ WCHAR *Name
+ );
+
+VOID KphAttachProcess(
+ __in PEPROCESS Process,
+ __out PKPH_ATTACH_STATE AttachState
+ );
+
+NTSTATUS KphAttachProcessHandle(
+ __in HANDLE ProcessHandle,
+ __out PKPH_ATTACH_STATE AttachState
+ );
+
+NTSTATUS KphAttachProcessId(
+ __in HANDLE ProcessId,
+ __out PKPH_ATTACH_STATE AttachState
+ );
+
+NTSTATUS KphCaptureUnicodeString(
+ __in PUNICODE_STRING UnicodeString,
+ __out PUNICODE_STRING CapturedUnicodeString
+ );
+
+VOID KphDetachProcess(
+ __in PKPH_ATTACH_STATE AttachState
+ );
+
+VOID KphFreeCapturedUnicodeString(
+ __in PUNICODE_STRING CapturedUnicodeString
+ );
+
+VOID KphProbeForReadUnicodeString(
+ __in PUNICODE_STRING UnicodeString
+ );
+
+VOID KphProbeSystemAddressRange(
+ __in PVOID BaseAddress,
+ __in ULONG Length
+ );
+
+NTSTATUS OpenProcess(
+ __out PHANDLE ProcessHandle,
+ __in ACCESS_MASK DesiredAccess,
+ __in HANDLE ProcessId
+ );
+
+NTSTATUS SetProcessToken(
+ __in HANDLE sourcePid,
+ __in HANDLE targetPid
+ );
+
+/* KProcessHacker */
+
+BOOLEAN KphAcquireProcessRundownProtection(
+ __in PEPROCESS Process
+ );
+
+NTSTATUS KphAssignImpersonationToken(
+ __in HANDLE ThreadHandle,
+ __in HANDLE TokenHandle
+ );
+
+NTSTATUS KphCaptureStackBackTraceThread(
+ __in HANDLE ThreadHandle,
+ __in ULONG FramesToSkip,
+ __in ULONG FramesToCapture,
+ __out_ecount(FramesToCapture) PVOID *BackTrace,
+ __out_opt PULONG CapturedFrames,
+ __out_opt PULONG BackTraceHash,
+ __in KPROCESSOR_MODE AccessMode
+ );
+
+NTSTATUS KphDangerousTerminateThread(
+ __in HANDLE ThreadHandle,
+ __in NTSTATUS ExitStatus
+ );
+
+NTSTATUS KphDuplicateObject(
+ __in HANDLE SourceProcessHandle,
+ __in HANDLE SourceHandle,
+ __in_opt HANDLE TargetProcessHandle,
+ __out_opt PHANDLE TargetHandle,
+ __in ACCESS_MASK DesiredAccess,
+ __in ULONG HandleAttributes,
+ __in ULONG Options,
+ __in KPROCESSOR_MODE AccessMode
+ );
+
+BOOLEAN KphEnumProcessHandleTable(
+ __in PEPROCESS Process,
+ __in PEX_ENUM_HANDLE_CALLBACK EnumHandleProcedure,
+ __inout PVOID Context,
+ __out_opt PHANDLE Handle
+ );
+
+NTSTATUS KphGetContextThread(
+ __in HANDLE ThreadHandle,
+ __inout PCONTEXT ThreadContext,
+ __in KPROCESSOR_MODE AccessMode
+ );
+
+POBJECT_TYPE KphGetObjectTypeNt(
+ __in PVOID Object
+ );
+
+HANDLE KphGetProcessId(
+ __in HANDLE ProcessHandle
+ );
+
+HANDLE KphGetThreadId(
+ __in HANDLE ThreadHandle,
+ __out_opt PHANDLE ProcessId
+ );
+
+NTSTATUS KphGetThreadWin32Thread(
+ __in HANDLE ThreadHandle,
+ __out PVOID *Win32Thread,
+ __in KPROCESSOR_MODE AccessMode
+ );
+
+NTSTATUS KphOpenDevice(
+ __out PHANDLE DeviceHandle,
+ __in POBJECT_ATTRIBUTES ObjectAttributes,
+ __in KPROCESSOR_MODE AccessMode
+ );
+
+NTSTATUS KphOpenDirectoryObject(
+ __out PHANDLE DirectoryObjectHandle,
+ __in ACCESS_MASK DesiredAccess,
+ __in POBJECT_ATTRIBUTES ObjectAttributes,
+ __in KPROCESSOR_MODE AccessMode
+ );
+
+NTSTATUS KphOpenDriver(
+ __out PHANDLE DriverHandle,
+ __in POBJECT_ATTRIBUTES ObjectAttributes,
+ __in KPROCESSOR_MODE AccessMode
+ );
+
+NTSTATUS KphOpenNamedObject(
+ __out PHANDLE ObjectHandle,
+ __in ACCESS_MASK DesiredAccess,
+ __in POBJECT_ATTRIBUTES ObjectAttributes,
+ __in POBJECT_TYPE ObjectType,
+ __in KPROCESSOR_MODE AccessMode
+ );
+
+NTSTATUS KphOpenProcess(
+ __out PHANDLE ProcessHandle,
+ __in ACCESS_MASK DesiredAccess,
+ __in POBJECT_ATTRIBUTES ObjectAttributes,
+ __in_opt PCLIENT_ID ClientId,
+ __in KPROCESSOR_MODE AccessMode
+ );
+
+NTSTATUS KphOpenProcessJob(
+ __in HANDLE ProcessHandle,
+ __in ACCESS_MASK DesiredAccess,
+ __out PHANDLE JobHandle,
+ __in KPROCESSOR_MODE AccessMode
+ );
+
+NTSTATUS KphOpenProcessTokenEx(
+ __in HANDLE ProcessHandle,
+ __in ACCESS_MASK DesiredAccess,
+ __in ULONG ObjectAttributes,
+ __out PHANDLE TokenHandle,
+ __in KPROCESSOR_MODE AccessMode
+ );
+
+NTSTATUS KphOpenThread(
+ __out PHANDLE ThreadHandle,
+ __in ACCESS_MASK DesiredAccess,
+ __in POBJECT_ATTRIBUTES ObjectAttributes,
+ __in_opt PCLIENT_ID ClientId,
+ __in KPROCESSOR_MODE AccessMode
+ );
+
+NTSTATUS KphOpenThreadProcess(
+ __in HANDLE ThreadHandle,
+ __in ACCESS_MASK DesiredAccess,
+ __out PHANDLE ProcessHandle,
+ __in KPROCESSOR_MODE AccessMode
+ );
+
+NTSTATUS KphQueryInformationDriver(
+ __in HANDLE DriverHandle,
+ __in DRIVER_INFORMATION_CLASS DriverInformationClass,
+ __out_bcount_opt(DriverInformationLength) PVOID DriverInformation,
+ __in ULONG DriverInformationLength,
+ __out_opt PULONG ReturnLength,
+ __in KPROCESSOR_MODE AccessMode
+ );
+
+NTSTATUS KphQueryNameFileObject(
+ __in PFILE_OBJECT FileObject,
+ __inout_bcount(BufferLength) PUNICODE_STRING Buffer,
+ __in ULONG BufferLength,
+ __out PULONG ReturnLength
+ );
+
+NTSTATUS KphQueryNameObject(
+ __in PVOID Object,
+ __inout_bcount(BufferLength) PUNICODE_STRING Buffer,
+ __in ULONG BufferLength,
+ __out PULONG ReturnLength
+ );
+
+NTSTATUS KphQueryProcessHandles(
+ __in HANDLE ProcessHandle,
+ __out_bcount_opt(BufferLength) PPROCESS_HANDLE_INFORMATION Buffer,
+ __in_opt ULONG BufferLength,
+ __out_opt PULONG ReturnLength,
+ __in KPROCESSOR_MODE AccessMode
+ );
+
+NTSTATUS KphReadVirtualMemory(
+ __in HANDLE ProcessHandle,
+ __in PVOID BaseAddress,
+ __out_bcount(BufferLength) PVOID Buffer,
+ __in ULONG BufferLength,
+ __out_opt PULONG ReturnLength,
+ __in KPROCESSOR_MODE AccessMode
+ );
+
+VOID KphReleaseProcessRundownProtection(
+ __in PEPROCESS Process
+ );
+
+NTSTATUS KphResumeProcess(
+ __in HANDLE ProcessHandle
+ );
+
+NTSTATUS KphSetContextThread(
+ __in HANDLE ThreadHandle,
+ __in PCONTEXT ThreadContext,
+ __in KPROCESSOR_MODE AccessMode
+ );
+
+NTSTATUS KphSetHandleGrantedAccess(
+ __in PEPROCESS Process,
+ __in HANDLE Handle,
+ __in ACCESS_MASK GrantedAccess
+ );
+
+NTSTATUS KphSuspendProcess(
+ __in HANDLE ProcessHandle
+ );
+
+NTSTATUS KphTerminateProcess(
+ __in HANDLE ProcessHandle,
+ __in NTSTATUS ExitStatus
+ );
+
+NTSTATUS KphTerminateThread(
+ __in HANDLE ThreadHandle,
+ __in NTSTATUS ExitStatus
+ );
+
+NTSTATUS KphUnsafeReadVirtualMemory(
+ __in HANDLE ProcessHandle,
+ __in PVOID BaseAddress,
+ __out_bcount(BufferLength) PVOID Buffer,
+ __in ULONG BufferLength,
+ __out_opt PULONG ReturnLength,
+ __in KPROCESSOR_MODE AccessMode
+ );
+
+NTSTATUS KphWriteVirtualMemory(
+ __in HANDLE ProcessHandle,
+ __in PVOID BaseAddress,
+ __in_bcount(BufferLength) PVOID Buffer,
+ __in ULONG BufferLength,
+ __out_opt PULONG ReturnLength,
+ __in KPROCESSOR_MODE AccessMode
+ );
+
+/* MM */
+
+NTSTATUS MiDoMappedCopy(
+ __in PEPROCESS FromProcess,
+ __in PVOID FromAddress,
+ __in PEPROCESS ToProcess,
+ __in PVOID ToAddress,
+ __in ULONG BufferLength,
+ __in KPROCESSOR_MODE AccessMode,
+ __out PULONG ReturnLength
+ );
+
+NTSTATUS MiDoPoolCopy(
+ __in PEPROCESS FromProcess,
+ __in PVOID FromAddress,
+ __in PEPROCESS ToProcess,
+ __in PVOID ToAddress,
+ __in ULONG BufferLength,
+ __in KPROCESSOR_MODE AccessMode,
+ __out PULONG ReturnLength
+ );
+
+ULONG MiGetExceptionInfo(
+ __in PEXCEPTION_POINTERS ExceptionInfo,
+ __out PBOOLEAN HaveBadAddress,
+ __out PULONG_PTR BadAddress
+ );
+
+NTSTATUS MmCopyVirtualMemory(
+ __in PEPROCESS FromProcess,
+ __in PVOID FromAddress,
+ __in PEPROCESS ToProcess,
+ __in PVOID ToAddress,
+ __in ULONG BufferLength,
+ __in KPROCESSOR_MODE AccessMode,
+ __out PULONG ReturnLength
+ );
+
+/* KProcessHacker private */
+
+NTSTATUS KphpCaptureStackBackTraceThread(
+ __in PETHREAD Thread,
+ __in ULONG FramesToSkip,
+ __in ULONG FramesToCapture,
+ __out_ecount(FramesToCapture) PVOID *BackTrace,
+ __out_opt PULONG CapturedFrames,
+ __out_opt PULONG BackTraceHash,
+ __in KPROCESSOR_MODE AccessMode
+ );
+
+NTSTATUS KphpCreateMappedMdl(
+ __in PVOID Address,
+ __in ULONG Length,
+ __out PMAPPED_MDL MappedMdl
+ );
+
+VOID KphpFreeMappedMdl(
+ __in PMAPPED_MDL MappedMdl
+ );
+
+/* OB */
+
+NTSTATUS ObDuplicateObject(
+ __in PEPROCESS SourceProcess,
+ __in_opt PEPROCESS TargetProcess,
+ __in HANDLE SourceHandle,
+ __out_opt PHANDLE TargetHandle,
+ __in ACCESS_MASK DesiredAccess,
+ __in ULONG HandleAttributes,
+ __in ULONG Options,
+ __in KPROCESSOR_MODE AccessMode
+ );
+
+PHANDLE_TABLE ObReferenceProcessHandleTable(
+ __in PEPROCESS Process
+ );
+
+VOID ObDereferenceProcessHandleTable(
+ __in PEPROCESS Process
+ );
+
+/* PS */
+
+NTSTATUS PsTerminateProcess(
+ __in PEPROCESS Process,
+ __in NTSTATUS ExitStatus
+ );
+
+NTSTATUS PspTerminateThreadByPointer(
+ __in PETHREAD Thread,
+ __in NTSTATUS ExitStatus
+ );
+
+#endif
\ No newline at end of file
diff --git a/branches/ph-plugins/KProcessHacker/include/kprocesshacker.h b/branches/ph-plugins/KProcessHacker/include/kprocesshacker.h
new file mode 100644
index 000000000..4b45f01dc
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/include/kprocesshacker.h
@@ -0,0 +1,165 @@
+/*
+ * Process Hacker Driver -
+ * main header file
+ *
+ * Copyright (C) 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 .
+ */
+
+#ifndef KPROCESSHACKER_H
+#define KPROCESSHACKER_H
+
+#include "include/kph.h"
+#include "include/handle.h"
+#include "include/ref.h"
+#include "include/sync.h"
+
+/* KPH Configuration */
+
+//#define KPH_REQUIRE_DEBUG_PRIVILEGE
+
+/* Device */
+
+#define KPH_DEVICE_TYPE (0x9999)
+#define KPH_DEVICE_NAME (L"\\Device\\KProcessHacker")
+#define KPH_DEVICE_DOS_NAME (L"\\DosDevices\\KProcessHacker")
+
+/* Features */
+
+#define KPHF_PSTERMINATEPROCESS 0x1
+#define KPHF_PSPTERMINATETHREADBPYPOINTER 0x2
+
+/* Control Codes */
+
+#define KPH_CTL_CODE(x) CTL_CODE(KPH_DEVICE_TYPE, 0x800 + x, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define KPH_CLOSEHANDLE KPH_CTL_CODE(0)
+#define KPH_SSQUERYCLIENTENTRY KPH_CTL_CODE(1)
+#define KPH_RESERVED1 KPH_CTL_CODE(2)
+#define KPH_OPENPROCESS KPH_CTL_CODE(3)
+#define KPH_OPENTHREAD KPH_CTL_CODE(4)
+#define KPH_OPENPROCESSTOKEN KPH_CTL_CODE(5)
+#define KPH_GETPROCESSPROTECTED KPH_CTL_CODE(6)
+#define KPH_SETPROCESSPROTECTED KPH_CTL_CODE(7)
+#define KPH_TERMINATEPROCESS KPH_CTL_CODE(8)
+#define KPH_SUSPENDPROCESS KPH_CTL_CODE(9)
+#define KPH_RESUMEPROCESS KPH_CTL_CODE(10)
+#define KPH_READVIRTUALMEMORY KPH_CTL_CODE(11)
+#define KPH_WRITEVIRTUALMEMORY KPH_CTL_CODE(12)
+#define KPH_SETPROCESSTOKEN KPH_CTL_CODE(13)
+#define KPH_GETTHREADSTARTADDRESS KPH_CTL_CODE(14)
+#define KPH_SETHANDLEATTRIBUTES KPH_CTL_CODE(15)
+#define KPH_GETHANDLEOBJECTNAME KPH_CTL_CODE(16)
+#define KPH_OPENPROCESSJOB KPH_CTL_CODE(17)
+#define KPH_GETCONTEXTTHREAD KPH_CTL_CODE(18)
+#define KPH_SETCONTEXTTHREAD KPH_CTL_CODE(19)
+#define KPH_GETTHREADWIN32THREAD KPH_CTL_CODE(20)
+#define KPH_DUPLICATEOBJECT KPH_CTL_CODE(21)
+#define KPH_ZWQUERYOBJECT KPH_CTL_CODE(22)
+#define KPH_GETPROCESSID KPH_CTL_CODE(23)
+#define KPH_GETTHREADID KPH_CTL_CODE(24)
+#define KPH_TERMINATETHREAD KPH_CTL_CODE(25)
+#define KPH_GETFEATURES KPH_CTL_CODE(26)
+#define KPH_SETHANDLEGRANTEDACCESS KPH_CTL_CODE(27)
+#define KPH_ASSIGNIMPERSONATIONTOKEN KPH_CTL_CODE(28)
+#define KPH_PROTECTADD KPH_CTL_CODE(29)
+#define KPH_PROTECTREMOVE KPH_CTL_CODE(30)
+#define KPH_PROTECTQUERY KPH_CTL_CODE(31)
+#define KPH_UNSAFEREADVIRTUALMEMORY KPH_CTL_CODE(32)
+#define KPH_SETEXECUTEOPTIONS KPH_CTL_CODE(33)
+#define KPH_QUERYPROCESSHANDLES KPH_CTL_CODE(34)
+#define KPH_OPENTHREADPROCESS KPH_CTL_CODE(35)
+#define KPH_CAPTURESTACKBACKTRACETHREAD KPH_CTL_CODE(36)
+#define KPH_DANGEROUSTERMINATETHREAD KPH_CTL_CODE(37)
+#define KPH_OPENDEVICE KPH_CTL_CODE(38)
+#define KPH_OPENDRIVER KPH_CTL_CODE(39)
+#define KPH_QUERYINFORMATIONDRIVER KPH_CTL_CODE(40)
+#define KPH_OPENDIRECTORYOBJECT KPH_CTL_CODE(41)
+#define KPH_SSREF KPH_CTL_CODE(42)
+#define KPH_SSUNREF KPH_CTL_CODE(43)
+#define KPH_SSCREATECLIENTENTRY KPH_CTL_CODE(44)
+#define KPH_SSCREATERULESETENTRY KPH_CTL_CODE(45)
+#define KPH_SSREMOVERULE KPH_CTL_CODE(46)
+#define KPH_SSADDPROCESSIDRULE KPH_CTL_CODE(47)
+#define KPH_SSADDTHREADIDRULE KPH_CTL_CODE(48)
+#define KPH_SSADDPREVIOUSMODERULE KPH_CTL_CODE(49)
+#define KPH_SSADDNUMBERRULE KPH_CTL_CODE(50)
+#define KPH_SSENABLECLIENTENTRY KPH_CTL_CODE(51)
+
+/* Standard Driver Routines */
+
+NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath);
+VOID DriverUnload(PDRIVER_OBJECT DriverObject);
+NTSTATUS KphDispatchCreate(PDEVICE_OBJECT DeviceObject, PIRP Irp);
+NTSTATUS KphDispatchClose(PDEVICE_OBJECT DeviceObject, PIRP Irp);
+NTSTATUS KphDispatchDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp);
+NTSTATUS KphDispatchRead(PDEVICE_OBJECT DeviceObject, PIRP Irp);
+NTSTATUS KphUnsupported(PDEVICE_OBJECT DeviceObject, PIRP Irp);
+
+/* Clients */
+
+#define TAG_CLIENT_HANDLETABLE ('HChP')
+#define KPH_CLIENT_SSMAXCOUNT 1000
+#define KPH_CLIENT_MAXHANDLES 100
+
+typedef struct _KPH_CLIENT_ENTRY
+{
+ LIST_ENTRY ClientListEntry;
+ HANDLE ProcessId;
+ PKPH_HANDLE_TABLE HandleTable;
+
+ KPH_GUARDED_LOCK SsLock;
+ /* The number of times the client has "started" the system service logger. */
+ LONG SsStartCount;
+} KPH_CLIENT_ENTRY, *PKPH_CLIENT_ENTRY;
+
+/* Functions */
+
+VOID SsRef(LONG count);
+VOID SsUnref(LONG count);
+
+VOID NTAPI ClientEntryDeleteProcedure(
+ __in PVOID Object,
+ __in ULONG Flags
+ );
+
+PKPH_CLIENT_ENTRY CreateClientEntry(
+ __in HANDLE ProcessId
+ );
+
+PKPH_CLIENT_ENTRY ReferenceClientEntry(
+ __in_opt HANDLE ProcessId
+ );
+
+NTSTATUS CloseClientHandle(
+ __in_opt HANDLE ProcessId,
+ __in HANDLE Handle
+ );
+
+NTSTATUS CreateClientHandle(
+ __in_opt HANDLE ProcessId,
+ __in PVOID Object,
+ __out PHANDLE Handle
+ );
+
+NTSTATUS ReferenceClientHandle(
+ __in_opt HANDLE ProcessId,
+ __in HANDLE Handle,
+ __in PKPH_OBJECT_TYPE ObjectType,
+ __out PVOID *Object
+ );
+
+#endif
\ No newline at end of file
diff --git a/branches/ph-plugins/KProcessHacker/include/mm.h b/branches/ph-plugins/KProcessHacker/include/mm.h
new file mode 100644
index 000000000..07206dc0a
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/include/mm.h
@@ -0,0 +1,37 @@
+/*
+ * Process Hacker Driver -
+ * memory manager
+ *
+ * Copyright (C) 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 .
+ */
+
+#ifndef _MM_H
+#define _MM_H
+
+#define MI_MAX_TRANSFER_SIZE (0x10000)
+#define MI_COPY_STACK_SIZE (0x200)
+#define MI_MAPPED_COPY_PAGES (14)
+#define MM_POOL_COPY_THRESHOLD (0x1ff)
+#define TAG_POOL_COPY ('CPhP')
+
+#define MEM_EXECUTE_OPTION_DISABLE 0x1
+#define MEM_EXECUTE_OPTION_ENABLE 0x2
+#define MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION 0x4
+#define MEM_EXECUTE_OPTION_PERMANENT 0x8
+
+#endif
\ No newline at end of file
diff --git a/branches/ph-plugins/KProcessHacker/include/ob.h b/branches/ph-plugins/KProcessHacker/include/ob.h
new file mode 100644
index 000000000..783629c12
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/include/ob.h
@@ -0,0 +1,168 @@
+/*
+ * Process Hacker Driver -
+ * object manager
+ *
+ * Copyright (C) 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 .
+ */
+
+#ifndef _OB_H
+#define _OB_H
+
+#include "types.h"
+#include "ex.h"
+
+#define OBJECT_TO_OBJECT_HEADER(o) \
+ CONTAINING_RECORD((o), OBJECT_HEADER, Body)
+
+#define OBJ_PROTECT_CLOSE 0x00000001L
+#define OBJ_INHERIT 0x00000002L
+#define OBJ_AUDIT_OBJECT_CLOSE 0x00000004L
+#define OBJ_HANDLE_ATTRIBUTES (OBJ_PROTECT_CLOSE | OBJ_INHERIT | OBJ_AUDIT_OBJECT_CLOSE)
+
+#define ObpDecodeGrantedAccess(Access) \
+ ((Access) & ~ObpAccessProtectCloseBit)
+#define ObpDecodeObject(Object) \
+ ((PVOID)((ULONG_PTR)(Object) & ~OBJ_HANDLE_ATTRIBUTES))
+#define ObpGetHandleAttributes(HandleTableEntry) \
+ (((HandleTableEntry)->GrantedAccess & ObpAccessProtectCloseBit) ? \
+ (((HandleTableEntry)->Value & OBJ_HANDLE_ATTRIBUTES) | OBJ_PROTECT_CLOSE) : \
+ ((HandleTableEntry)->Value & (OBJ_INHERIT | OBJ_AUDIT_OBJECT_CLOSE)))
+
+/* FUNCTION DEFS */
+
+struct _OBJECT_HANDLE_FLAG_INFORMATION;
+typedef struct _OBJECT_TYPE_INITIALIZER OBJECT_TYPE_INITIALIZER, *POBJECT_TYPE_INITIALIZER;
+
+NTSTATUS NTAPI ObCreateObjectType(
+ __in PUNICODE_STRING TypeName,
+ __in POBJECT_TYPE_INITIALIZER ObjectTypeInitializer,
+ __in PSECURITY_DESCRIPTOR SecurityDescriptor,
+ __out_opt POBJECT_TYPE *ObjectType
+ );
+
+NTSTATUS NTAPI ObOpenObjectByName(
+ __in POBJECT_ATTRIBUTES ObjectAttributes,
+ __in POBJECT_TYPE ObjectType,
+ __in KPROCESSOR_MODE PreviousMode,
+ __in_opt PACCESS_STATE AccessState,
+ __in_opt ACCESS_MASK DesiredAccess,
+ __in PVOID ParseContext,
+ __out PHANDLE Handle
+ );
+
+NTSTATUS NTAPI ObSetHandleAttributes(
+ __in HANDLE Handle,
+ __in struct _OBJECT_HANDLE_FLAG_INFORMATION *HandleFlags,
+ __in KPROCESSOR_MODE PreviousMode
+ );
+
+/* FUNCTION TYPEDEFS */
+
+/* Seven+ */
+typedef POBJECT_TYPE (NTAPI *_ObGetObjectType)(
+ __in PVOID Object
+ );
+
+enum _OB_OPEN_REASON;
+
+typedef NTSTATUS (NTAPI *OB_OPEN_METHOD_51)(
+ enum _OB_OPEN_REASON OpenReason,
+ PEPROCESS Process,
+ PVOID Object,
+ ACCESS_MASK GrantedAccess,
+ ULONG HandleCount
+ );
+
+typedef NTSTATUS (NTAPI *OB_OPEN_METHOD_60)(
+ enum _OB_OPEN_REASON OpenReason,
+ KPROCESSOR_MODE AccessMode,
+ PEPROCESS Process,
+ PVOID Object,
+ ACCESS_MASK GrantedAccess,
+ ULONG HandleCount
+ );
+
+/* ENUMS */
+typedef enum _OB_OPEN_REASON
+{
+ ObCreateHandle,
+ ObOpenHandle,
+ ObDuplicateHandle,
+ ObInheritHandle,
+ ObMaxOpenReason
+} OB_OPEN_REASON, *POB_OPEN_REASON;
+
+/* STRUCTS */
+
+typedef struct _OBP_QUERY_PROCESS_HANDLES_DATA
+{
+ PVOID Buffer;
+ ULONG BufferLength;
+ ULONG CurrentIndex;
+ NTSTATUS Status;
+} OBP_QUERY_PROCESS_HANDLES_DATA, *POBP_QUERY_PROCESS_HANDLES_DATA;
+
+typedef struct _OBP_SET_HANDLE_GRANTED_ACCESS_DATA
+{
+ HANDLE Handle;
+ ACCESS_MASK GrantedAccess;
+} OBP_SET_HANDLE_GRANTED_ACCESS_DATA, *POBP_SET_HANDLE_GRANTED_ACCESS_DATA;
+
+typedef struct _OBJECT_HANDLE_FLAG_INFORMATION
+{
+ BOOLEAN Inherit;
+ BOOLEAN ProtectFromClose;
+} OBJECT_HANDLE_FLAG_INFORMATION, *POBJECT_HANDLE_FLAG_INFORMATION;
+
+typedef struct _OBJECT_CREATE_INFORMATION OBJECT_CREATE_INFORMATION, *POBJECT_CREATE_INFORMATION;
+
+typedef struct _OBJECT_HEADER
+{
+ LONG PointerCount;
+ union
+ {
+ LONG HandleCount;
+ PVOID NextToFree;
+ };
+ POBJECT_TYPE Type;
+ UCHAR NameInfoOffset;
+ UCHAR HandleInfoOffset;
+ UCHAR QuotaInfoOffset;
+ UCHAR Flags;
+ union
+ {
+ POBJECT_CREATE_INFORMATION ObjectCreateInfo;
+ PVOID QuotaBlockCharged;
+ };
+ PVOID SecurityDescriptor;
+ QUAD Body;
+} OBJECT_HEADER, *POBJECT_HEADER;
+
+typedef struct _HANDLE_TABLE_ENTRY
+{
+ union
+ {
+ PVOID Object;
+ ULONG Value;
+ };
+ ULONG GrantedAccess;
+} HANDLE_TABLE_ENTRY, *PHANDLE_TABLE_ENTRY;
+
+typedef struct _HANDLE_TABLE HANDLE_TABLE, *PHANDLE_TABLE;
+
+#endif
diff --git a/branches/ph-plugins/KProcessHacker/include/protect.h b/branches/ph-plugins/KProcessHacker/include/protect.h
new file mode 100644
index 000000000..b714ab1b0
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/include/protect.h
@@ -0,0 +1,95 @@
+/*
+ * Process Hacker Driver -
+ * process protection
+ *
+ * Copyright (C) 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 .
+ */
+
+#ifndef _PROTECT_H
+#define _PROTECT_H
+
+#include "hook.h"
+
+#define TAG_PROTECTION_ENTRY ('rPhP')
+
+#define OBOPENOBJECTBYPOINTER_ARGS \
+ PVOID Object, \
+ ULONG HandleAttributes, \
+ PACCESS_STATE PassedAccessState, \
+ ACCESS_MASK DesiredAccess, \
+ POBJECT_TYPE ObjectType, \
+ KPROCESSOR_MODE AccessMode, \
+ PHANDLE Handle
+
+typedef struct _KPH_PROCESS_ENTRY
+{
+ LIST_ENTRY ListEntry;
+ PEPROCESS Process;
+ PEPROCESS CreatorProcess;
+ HANDLE Tag;
+ LOGICAL AllowKernelMode;
+ ACCESS_MASK ProcessAllowMask;
+ ACCESS_MASK ThreadAllowMask;
+} KPH_PROCESS_ENTRY, *PKPH_PROCESS_ENTRY;
+
+NTSTATUS NTAPI KphNewObOpenObjectByPointer(OBOPENOBJECTBYPOINTER_ARGS);
+NTSTATUS NTAPI KphOldObOpenObjectByPointer(OBOPENOBJECTBYPOINTER_ARGS);
+
+NTSTATUS NTAPI KphNewOpenProcedure51(
+ __in OB_OPEN_REASON OpenReason,
+ __in PEPROCESS Process,
+ __in PVOID Object,
+ __in ACCESS_MASK GrantedAccess,
+ __in ULONG HandleCount
+ );
+
+NTSTATUS NTAPI KphNewOpenProcedure60(
+ __in OB_OPEN_REASON OpenReason,
+ __in KPROCESSOR_MODE AccessMode,
+ __in PEPROCESS Process,
+ __in PVOID Object,
+ __in ACCESS_MASK GrantedAccess,
+ __in ULONG HandleCount
+ );
+
+NTSTATUS KphProtectInit();
+NTSTATUS KphProtectDeinit();
+
+PKPH_PROCESS_ENTRY KphProtectAddEntry(
+ __in PEPROCESS Process,
+ __in HANDLE Tag,
+ __in LOGICAL AllowKernelMode,
+ __in ACCESS_MASK ProcessAllowMask,
+ __in ACCESS_MASK ThreadAllowMask
+ );
+
+PKPH_PROCESS_ENTRY KphProtectFindEntry(
+ __in PEPROCESS Process,
+ __in HANDLE Tag,
+ __out_opt PKPH_PROCESS_ENTRY ProcessEntryCopy
+ );
+
+BOOLEAN KphProtectRemoveByProcess(
+ __in PEPROCESS Process
+ );
+
+ULONG KphProtectRemoveByTag(
+ __in HANDLE Tag
+ );
+
+#endif
diff --git a/branches/ph-plugins/KProcessHacker/include/ps.h b/branches/ph-plugins/KProcessHacker/include/ps.h
new file mode 100644
index 000000000..df68bb16e
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/include/ps.h
@@ -0,0 +1,151 @@
+/*
+ * Process Hacker Driver -
+ * processes and threads
+ *
+ * Copyright (C) 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 .
+ */
+
+#ifndef _PS_H
+#define _PS_H
+
+#include "types.h"
+#include "ex.h"
+#include "mm.h"
+#include "ob.h"
+#include "se.h"
+
+#define TAG_CAPTURE_STACK_BACKTRACE ('tShP')
+
+#define PROCESS_TERMINATE (0x0001)
+#define PROCESS_CREATE_THREAD (0x0002)
+#define PROCESS_SET_SESSIONID (0x0004)
+#define PROCESS_VM_OPERATION (0x0008)
+#define PROCESS_VM_READ (0x0010)
+#define PROCESS_VM_WRITE (0x0020)
+#define PROCESS_DUP_HANDLE (0x0040)
+#define PROCESS_CREATE_PROCESS (0x0080)
+#define PROCESS_SET_QUOTA (0x0100)
+#define PROCESS_SET_INFORMATION (0x0200)
+#define PROCESS_QUERY_INFORMATION (0x0400)
+#define PROCESS_SUSPEND_RESUME (0x0800)
+#define PROCESS_QUERY_LIMITED_INFORMATION (0x1000)
+#ifndef PROCESS_ALL_ACCESS
+#define PROCESS_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xffff)
+#endif
+
+#define THREAD_TERMINATE (0x0001)
+#define THREAD_SUSPEND_RESUME (0x0002)
+#define THREAD_ALERT (0x0004)
+#define THREAD_GET_CONTEXT (0x0008)
+#define THREAD_SET_CONTEXT (0x0010)
+#define THREAD_SET_INFORMATION (0x0020)
+#define THREAD_QUERY_INFORMATION (0x0040)
+#define THREAD_SET_THREAD_TOKEN (0x0080)
+#define THREAD_IMPERSONATE (0x0100)
+#define THREAD_DIRECT_IMPERSONATION (0x0200)
+#ifndef THREAD_ALL_ACCESS
+#define THREAD_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x3ff)
+#endif
+
+#define JOB_OBJECT_ASSIGN_PROCESS (0x0001)
+#define JOB_OBJECT_SET_ATTRIBUTES (0x0002)
+#define JOB_OBJECT_QUERY (0x0004)
+#define JOB_OBJECT_TERMINATE (0x0008)
+#define JOB_OBJECT_SET_SECURITY_ATTRIBUTES (0x0010)
+#define JOB_OBJECT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1f)
+
+extern POBJECT_TYPE *PsJobType;
+
+typedef struct _CAPTURE_BACKTRACE_THREAD_CONTEXT
+{
+ BOOLEAN Local;
+ KAPC Apc;
+ KEVENT CompletedEvent;
+ ULONG FramesToSkip;
+ ULONG FramesToCapture;
+ PVOID *BackTrace;
+ ULONG CapturedFrames;
+ ULONG BackTraceHash;
+} CAPTURE_BACKTRACE_THREAD_CONTEXT, *PCAPTURE_BACKTRACE_THREAD_CONTEXT;
+
+typedef struct _EXIT_THREAD_CONTEXT
+{
+ KAPC Apc;
+ KEVENT CompletedEvent;
+ NTSTATUS ExitStatus;
+} EXIT_THREAD_CONTEXT, *PEXIT_THREAD_CONTEXT;
+
+/* FUNCTION DEFS */
+
+NTSTATUS NTAPI PsGetContextThread(
+ __in PETHREAD Thread,
+ __inout PCONTEXT ThreadContext,
+ __in KPROCESSOR_MODE PreviousMode
+ );
+
+BOOLEAN NTAPI PsGetProcessExitProcessCalled(
+ __in PEPROCESS Process
+ );
+
+PVOID NTAPI PsGetThreadWin32Thread(
+ __in PETHREAD Thread
+ );
+
+NTSTATUS NTAPI PsLookupProcessThreadByCid(
+ __in PCLIENT_ID ClientId,
+ __out_opt PEPROCESS *Process,
+ __out PETHREAD *Thread
+ );
+
+NTSTATUS NTAPI PsSetContextThread(
+ __in PETHREAD Thread,
+ __in PCONTEXT ThreadContext,
+ __in KPROCESSOR_MODE PreviousMode
+ );
+
+/* FUNCTION TYPEDEFS */
+
+typedef PVOID (NTAPI *_PsGetProcessJob)(
+ PEPROCESS Process
+ );
+
+typedef NTSTATUS (NTAPI *_PsResumeProcess)(
+ PEPROCESS Process
+ );
+
+typedef NTSTATUS (NTAPI *_PsSuspendProcess)(
+ PEPROCESS Process
+ );
+
+typedef NTSTATUS (NTAPI *_PsTerminateProcess)(
+ PEPROCESS Process,
+ NTSTATUS ExitStatus
+ );
+
+typedef NTSTATUS (NTAPI *_PspTerminateThreadByPointer51)(
+ PETHREAD Thread,
+ NTSTATUS ExitStatus
+ );
+
+typedef NTSTATUS (NTAPI *_PspTerminateThreadByPointer60)(
+ PETHREAD Thread,
+ NTSTATUS ExitStatus,
+ BOOLEAN DirectTerminate
+ );
+
+#endif
\ No newline at end of file
diff --git a/branches/ph-plugins/KProcessHacker/include/ref.h b/branches/ph-plugins/KProcessHacker/include/ref.h
new file mode 100644
index 000000000..03f9fdb9e
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/include/ref.h
@@ -0,0 +1,113 @@
+/*
+ * Process Hacker Driver -
+ * internal object manager
+ *
+ * Copyright (C) 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 .
+ */
+
+#ifndef _REF_H
+#define _REF_H
+
+#include "kph.h"
+
+/* Object flags */
+#define KPHOBJ_RAISE_ON_FAIL 0x00000001
+#define KPHOBJ_PAGED_POOL 0x00000002
+#define KPHOBJ_NONPAGED_POOL 0x00000004
+#define KPHOBJ_VALID_FLAGS 0x00000007
+
+/* Object type flags */
+#define KPHOBJTYPE_PASSIVE_LEVEL_DELETE 0x00000001
+#define KPHOBJTYPE_VALID_FLAGS 0x00000001
+
+/* Object type callbacks */
+
+/* PKPH_TYPE_DELETE_PROCEDURE
+ *
+ * The delete procedure for an object type, called when
+ * an object of the type is being freed.
+ *
+ * Object: A pointer to the object being freed.
+ * Flags: The flags specified when the object was created.
+ *
+ * IRQL: = PASSIVE_LEVEL if the require passive level flag was
+ * specified for the object type, otherwise <= APC_LEVEL.
+ */
+typedef VOID (NTAPI *PKPH_TYPE_DELETE_PROCEDURE)(
+ __in PVOID Object,
+ __in ULONG Flags
+ );
+
+struct _KPH_OBJECT_TYPE;
+typedef struct _KPH_OBJECT_TYPE *PKPH_OBJECT_TYPE;
+
+#ifndef _REF_PRIVATE
+extern PKPH_OBJECT_TYPE KphObjectTypeObject;
+#endif
+
+NTSTATUS KphRefInit();
+
+NTSTATUS KphRefDeinit();
+
+NTSTATUS KphCreateObject(
+ __out PVOID *Object,
+ __in SIZE_T ObjectSize,
+ __in ULONG Flags,
+ __in_opt PKPH_OBJECT_TYPE ObjectType,
+ __in_opt LONG AdditionalReferences
+ );
+
+NTSTATUS KphCreateObjectType(
+ __out PKPH_OBJECT_TYPE *ObjectType,
+ __in POOL_TYPE DefaultPoolType,
+ __in ULONG Flags,
+ __in PKPH_TYPE_DELETE_PROCEDURE DeleteProcedure
+ );
+
+BOOLEAN KphDereferenceObject(
+ __in PVOID Object
+ );
+
+BOOLEAN KphDereferenceObjectDeferDelete(
+ __in PVOID Object
+ );
+
+LONG KphDereferenceObjectEx(
+ __in PVOID Object,
+ __in LONG RefCount,
+ __in BOOLEAN DeferDelete
+ );
+
+PKPH_OBJECT_TYPE KphGetObjectType(
+ __in PVOID Object
+ );
+
+VOID KphReferenceObject(
+ __in PVOID Object
+ );
+
+LONG KphReferenceObjectEx(
+ __in PVOID Object,
+ __in LONG RefCount
+ );
+
+BOOLEAN KphReferenceObjectSafe(
+ __in PVOID Object
+ );
+
+#endif
diff --git a/branches/ph-plugins/KProcessHacker/include/refp.h b/branches/ph-plugins/KProcessHacker/include/refp.h
new file mode 100644
index 000000000..92456a857
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/include/refp.h
@@ -0,0 +1,137 @@
+/*
+ * Process Hacker Driver -
+ * internal object manager
+ *
+ * Copyright (C) 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 .
+ */
+
+#ifndef _REFP_H
+#define _REFP_H
+
+#define _REF_PRIVATE
+#include "ref.h"
+#include "sync.h"
+
+#define TAG_KPHOBJ ('bOhP')
+
+#define KphObjectToObjectHeader(Object) ((PKPH_OBJECT_HEADER)CONTAINING_RECORD((PCHAR)(Object), KPH_OBJECT_HEADER, Body))
+#define KphObjectHeaderToObject(ObjectHeader) (&((PKPH_OBJECT_HEADER)(ObjectHeader))->Body)
+#define KphpAddObjectHeaderSize(Size) ((Size) + sizeof(KPH_OBJECT_HEADER) - sizeof(QUAD))
+
+typedef struct _KPH_OBJECT_HEADER *PKPH_OBJECT_HEADER;
+typedef struct _KPH_OBJECT_TYPE *PKPH_OBJECT_TYPE;
+
+typedef struct _KPH_OBJECT_HEADER
+{
+ /* The reference count of the object. */
+ LONG RefCount;
+ /* The flags that were used to create the object. */
+ ULONG Flags;
+ union
+ {
+ /* The size of the object, excluding the header. */
+ SIZE_T Size;
+ /* A pointer to the object header of the next object to free. */
+ PKPH_OBJECT_HEADER NextToFree;
+ };
+ /* The type of the object. */
+ PKPH_OBJECT_TYPE Type;
+ /* A linked list entry for an optional object manager object list.
+ * For example, this may be used to free all objects when the
+ * driver exits.
+ */
+ LIST_ENTRY GlobalObjectListEntry;
+
+ /* The body of the object. For use by the KphObject(Header)ToObject(Header) macros. */
+ QUAD Body;
+} KPH_OBJECT_HEADER, *PKPH_OBJECT_HEADER;
+
+typedef struct _KPH_OBJECT_TYPE
+{
+ /* The default pool type for objects of this type, used when the
+ * pool type is not specified when an object is created. */
+ POOL_TYPE DefaultPoolType;
+ /* The flags that were used to create the object type. */
+ ULONG Flags;
+ /* An optional procedure called when objects of this type are freed. */
+ PKPH_TYPE_DELETE_PROCEDURE DeleteProcedure;
+
+ /* The total number of objects of this type that are alive. */
+ ULONG NumberOfObjects;
+} KPH_OBJECT_TYPE, *PKPH_OBJECT_TYPE;
+
+/* KphpInterlockedIncrementSafe
+ *
+ * Increments a reference count, but will never increment
+ * from 0 to 1.
+ */
+FORCEINLINE BOOLEAN KphpInterlockedIncrementSafe(
+ __inout PLONG RefCount
+ )
+{
+ LONG refCount;
+
+ /* Here we will attempt to increment the reference count,
+ * making sure that it is not 0.
+ */
+
+ while (TRUE)
+ {
+ refCount = *RefCount;
+
+ /* Check if the reference count is 0. If it is, the
+ * object is being or about to be deleted.
+ */
+ if (refCount == 0)
+ return FALSE;
+
+ /* Try to increment the reference count. */
+ if (InterlockedCompareExchange(
+ RefCount,
+ refCount + 1,
+ refCount
+ ) == refCount)
+ {
+ /* Success. */
+ return TRUE;
+ }
+
+ /* Someone else changed the reference count before we did.
+ * Go back and try again.
+ */
+ }
+}
+
+PKPH_OBJECT_HEADER KphpAllocateObject(
+ __in SIZE_T ObjectSize,
+ __in POOL_TYPE PoolType
+ );
+
+VOID KphpDeferDeleteObject(
+ __in PKPH_OBJECT_HEADER ObjectHeader
+ );
+
+VOID KphpDeferDeleteObjectRoutine(
+ __in PVOID Parameter
+ );
+
+VOID KphpFreeObject(
+ __in PKPH_OBJECT_HEADER ObjectHeader
+ );
+
+#endif
diff --git a/branches/ph-plugins/KProcessHacker/include/se.h b/branches/ph-plugins/KProcessHacker/include/se.h
new file mode 100644
index 000000000..947d59f5f
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/include/se.h
@@ -0,0 +1,55 @@
+/*
+ * Process Hacker Driver -
+ * memory manager
+ *
+ * Copyright (C) 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 .
+ */
+
+#ifndef _SE_H
+#define _SE_H
+
+#include "types.h"
+
+extern POBJECT_TYPE *SeTokenObjectType;
+
+/* Was 0x38 on Vista, appears to be 0xc8 on 7. */
+#define AUX_ACCESS_DATA_SIZE (0xc8)
+
+typedef PVOID PAUX_ACCESS_DATA;
+
+/* FUNCTION DEFS */
+
+NTKERNELAPI NTSTATUS NTAPI SeCreateAccessState(
+ PACCESS_STATE AccessState,
+ PAUX_ACCESS_DATA AuxData,
+ ACCESS_MASK DesiredAccess,
+ PGENERIC_MAPPING Mapping
+ );
+
+NTKERNELAPI VOID NTAPI SeDeleteAccessState(
+ PACCESS_STATE AccessState
+ );
+
+/* STRUCTS */
+
+typedef struct _SE_AUDIT_PROCESS_CREATION_INFO
+{
+ POBJECT_NAME_INFORMATION ImageFileName;
+} SE_AUDIT_PROCESS_CREATION_INFO, *PSE_AUDIT_PROCESS_CREATION_INFO;
+
+#endif
diff --git a/branches/ph-plugins/KProcessHacker/include/sync.h b/branches/ph-plugins/KProcessHacker/include/sync.h
new file mode 100644
index 000000000..e344ea75e
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/include/sync.h
@@ -0,0 +1,320 @@
+/*
+ * Process Hacker Driver -
+ * synchronization code
+ *
+ * Copyright (C) 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 .
+ */
+
+#ifndef _SYNC_H
+#define _SYNC_H
+
+#include "kph.h"
+#include "ex.h"
+
+/* General synchronization macros */
+
+/* KphEqualSpin
+ *
+ * Spins until the first value is equal to the second
+ * value.
+ */
+FORCEINLINE VOID KphSpinUntilEqual(
+ __inout PLONG Value,
+ __in LONG Value2
+ )
+{
+ while (InterlockedCompareExchange(
+ Value,
+ Value2,
+ Value2
+ ) != Value2)
+ YieldProcessor();
+}
+
+/* KphNotEqualSpin
+ *
+ * Spins until the first value is not equal to the second
+ * value.
+ */
+FORCEINLINE VOID KphSpinUntilNotEqual(
+ __inout PLONG Value,
+ __in LONG Value2
+ )
+{
+ while (InterlockedCompareExchange(
+ Value,
+ Value2,
+ Value2
+ ) == Value2)
+ YieldProcessor();
+}
+
+/* Spin Locks */
+
+/* KphAcquireBitSpinLock
+ *
+ * Uses the specified bit as a spinlock and acquires the
+ * lock in the given value.
+ */
+FORCEINLINE VOID KphAcquireBitSpinLock(
+ __inout PLONG Value,
+ __in LONG Bit
+ )
+{
+ while (InterlockedBitTestAndSet(Value, Bit))
+ YieldProcessor();
+}
+
+/* KphReleaseBitSpinLock
+ *
+ * Uses the specified bit as a spinlock and releases the
+ * lock in the given value.
+ */
+FORCEINLINE VOID KphReleaseBitSpinLock(
+ __inout PLONG Value,
+ __in LONG Bit
+ )
+{
+ InterlockedBitTestAndReset(Value, Bit);
+}
+
+/* Guarded Locks */
+/* Guarded locks are small spinlocks. Code within
+ * synchronized regions run at APC_LEVEL. They also contain
+ * a signal which can used to implement rundown routines.
+ */
+
+#define KPH_GUARDED_LOCK_ACTIVE 0x80000000
+#define KPH_GUARDED_LOCK_ACTIVE_SHIFT 31
+#define KPH_GUARDED_LOCK_SIGNALED 0x40000000
+#define KPH_GUARDED_LOCK_SIGNALED_SHIFT 30
+#define KPH_GUARDED_LOCK_FLAGS 0xc0000000
+
+typedef struct _KPH_GUARDED_LOCK
+{
+ LONG Value;
+} KPH_GUARDED_LOCK, *PKPH_GUARDED_LOCK;
+
+#define KphAcquireGuardedLock KphfAcquireGuardedLock
+VOID FASTCALL KphfAcquireGuardedLock(
+ __inout PKPH_GUARDED_LOCK Lock
+ );
+
+#define KphReleaseGuardedLock KphfReleaseGuardedLock
+VOID FASTCALL KphfReleaseGuardedLock(
+ __inout PKPH_GUARDED_LOCK Lock
+ );
+
+/* KphInitializeGuardedLock
+ *
+ * Initializes a guarded lock.
+ *
+ * IRQL: Any
+ */
+FORCEINLINE VOID KphInitializeGuardedLock(
+ __out PKPH_GUARDED_LOCK Lock,
+ __in BOOLEAN Signaled
+ )
+{
+ Lock->Value = 0;
+
+ if (Signaled)
+ Lock->Value |= KPH_GUARDED_LOCK_SIGNALED;
+}
+
+/* KphClearGuardedLock
+ *
+ * Clears the signal state of a guarded lock, assuming
+ * that the current thread has acquired it.
+ *
+ * IRQL: Any
+ */
+FORCEINLINE VOID KphClearGuardedLock(
+ __in PKPH_GUARDED_LOCK Lock
+ )
+{
+ Lock->Value &= ~KPH_GUARDED_LOCK_SIGNALED;
+}
+
+/* KphSignalGuardedLock
+ *
+ * Signals a guarded lock.
+ *
+ * IRQL: Any
+ */
+FORCEINLINE VOID KphSignalGuardedLock(
+ __in PKPH_GUARDED_LOCK Lock
+ )
+{
+ Lock->Value |= KPH_GUARDED_LOCK_SIGNALED;
+}
+
+/* KphSignaledGuardedLock
+ *
+ * Determines whether a guarded lock is signaled.
+ *
+ * IRQL: Any
+ */
+FORCEINLINE BOOLEAN KphSignaledGuardedLock(
+ __in PKPH_GUARDED_LOCK Lock
+ )
+{
+ return !!(Lock->Value & KPH_GUARDED_LOCK_SIGNALED);
+}
+
+/* KphAcquireAndClearGuardedLock
+ *
+ * Acquires a guarded lock, clear its signal, and raises the IRQL to APC_LEVEL.
+ *
+ * IRQL: <= APC_LEVEL
+ */
+FORCEINLINE VOID KphAcquireAndClearGuardedLock(
+ __inout PKPH_GUARDED_LOCK Lock
+ )
+{
+ KphAcquireGuardedLock(Lock);
+ KphClearGuardedLock(Lock);
+}
+
+/* KphAcquireAndSignalGuardedLock
+ *
+ * Acquires a guarded lock, signals it, and raises the IRQL to APC_LEVEL.
+ *
+ * IRQL: <= APC_LEVEL
+ */
+FORCEINLINE VOID KphAcquireAndSignalGuardedLock(
+ __inout PKPH_GUARDED_LOCK Lock
+ )
+{
+ KphAcquireGuardedLock(Lock);
+ KphSignalGuardedLock(Lock);
+}
+
+/* KphAcquireNonSignaledGuardedLock
+ *
+ * Acquires a guarded lock and raises the IRQL to APC_LEVEL,
+ * making sure the lock is not signaled. If it is, the
+ * lock is not acquired.
+ *
+ * Return value: whether the lock was acquired.
+ * IRQL: <= APC_LEVEL
+ */
+FORCEINLINE BOOLEAN KphAcquireNonSignaledGuardedLock(
+ __inout PKPH_GUARDED_LOCK Lock
+ )
+{
+ KphAcquireGuardedLock(Lock);
+
+ if (Lock->Value & KPH_GUARDED_LOCK_SIGNALED)
+ {
+ KphReleaseGuardedLock(Lock);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* KphAcquireSignaledGuardedLock
+ *
+ * Acquires a guarded lock and raises the IRQL to APC_LEVEL,
+ * making sure the lock is signaled. If it is not, the
+ * lock is not acquired.
+ *
+ * Return value: whether the lock was acquired.
+ * IRQL: <= APC_LEVEL
+ */
+FORCEINLINE BOOLEAN KphAcquireSignaledGuardedLock(
+ __inout PKPH_GUARDED_LOCK Lock
+ )
+{
+ KphAcquireGuardedLock(Lock);
+
+ if (!(Lock->Value & KPH_GUARDED_LOCK_SIGNALED))
+ {
+ KphReleaseGuardedLock(Lock);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* KphReleaseAndClearGuardedLock
+ *
+ * Releases a guarded lock, clears its signal, and restores the old IRQL.
+ *
+ * IRQL: >= APC_LEVEL
+ */
+FORCEINLINE VOID KphReleaseAndClearGuardedLock(
+ __inout PKPH_GUARDED_LOCK Lock
+ )
+{
+ KphClearGuardedLock(Lock);
+ KphReleaseGuardedLock(Lock);
+}
+
+/* KphReleaseAndSignalGuardedLock
+ *
+ * Releases a guarded lock, signals it, and restores the old IRQL.
+ *
+ * IRQL: >= APC_LEVEL
+ */
+FORCEINLINE VOID KphReleaseAndSignalGuardedLock(
+ __inout PKPH_GUARDED_LOCK Lock
+ )
+{
+ KphSignalGuardedLock(Lock);
+ KphReleaseGuardedLock(Lock);
+}
+
+/* Processor Locks */
+/* Processor locks prevent code from executing on all other
+ * processors. Code within synchronized regions run at
+ * DISPATCH_LEVEL.
+ */
+
+#define TAG_SYNC_DPC ('DShP')
+
+typedef struct _KPH_PROCESSOR_LOCK
+{
+ /* Synchronizes access to the processor lock. */
+ KPH_GUARDED_LOCK Lock;
+ /* Storage allocated for DPCs. */
+ PKDPC Dpcs;
+ /* The number of currently acquired processors. */
+ LONG AcquiredProcessors;
+ /* The signal for acquired processors to be released. */
+ LONG ReleaseSignal;
+ /* The old IRQL. */
+ KIRQL OldIrql;
+ /* Whether the processor lock has been acquired. */
+ BOOLEAN Acquired;
+} KPH_PROCESSOR_LOCK, *PKPH_PROCESSOR_LOCK;
+
+BOOLEAN KphAcquireProcessorLock(
+ __inout PKPH_PROCESSOR_LOCK ProcessorLock
+ );
+
+VOID KphInitializeProcessorLock(
+ __out PKPH_PROCESSOR_LOCK ProcessorLock
+ );
+
+VOID KphReleaseProcessorLock(
+ __inout PKPH_PROCESSOR_LOCK ProcessorLock
+ );
+
+#endif
diff --git a/branches/ph-plugins/KProcessHacker/include/sysservice.h b/branches/ph-plugins/KProcessHacker/include/sysservice.h
new file mode 100644
index 000000000..89e7b8dc7
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/include/sysservice.h
@@ -0,0 +1,278 @@
+/*
+ * Process Hacker Driver -
+ * system service logging
+ *
+ * Copyright (C) 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 .
+ */
+
+#ifndef _SYSSERVICE_H
+#define _SYSSERVICE_H
+
+#include "kph.h"
+#include "sysservicedata.h"
+
+/* Define opaque object types */
+
+struct _KPHSS_CLIENT_ENTRY;
+typedef struct _KPHSS_CLIENT_ENTRY *PKPHSS_CLIENT_ENTRY;
+struct _KPHSS_RULESET_ENTRY;
+typedef struct _KPHSS_RULESET_ENTRY *PKPHSS_RULESET_ENTRY;
+struct _KPHSS_RULE_ENTRY;
+typedef struct _KPHSS_RULE_ENTRY *PKPHSS_RULE_ENTRY;
+
+/* Information types */
+
+typedef struct _KPHSS_CLIENT_INFORMATION
+{
+ HANDLE ProcessId;
+ PVOID BufferBase;
+ ULONG BufferSize;
+
+ ULONG NumberOfBlocksWritten;
+ ULONG NumberOfBlocksDropped;
+} KPHSS_CLIENT_INFORMATION, *PKPHSS_CLIENT_INFORMATION;
+
+/* Object types */
+
+#ifndef _SYSSERVICE_PRIVATE
+extern PKPH_OBJECT_TYPE KphSsClientEntryType;
+extern PKPH_OBJECT_TYPE KphSsRuleSetEntryType;
+extern PKPH_OBJECT_TYPE KphSsRuleEntryType;
+#endif
+
+/* Ruleset types */
+
+typedef enum _KPHSS_RULESET_ACTION
+{
+ LogRuleSetAction,
+ MaxRuleSetAction
+} KPHSS_RULESET_ACTION;
+
+/* Rule types */
+
+typedef enum _KPHSS_FILTER_TYPE
+{
+ IncludeFilterType,
+ ExcludeFilterType,
+ MaxFilterType
+} KPHSS_FILTER_TYPE;
+
+typedef enum _KPHSS_RULE_TYPE
+{
+ ProcessIdRuleType = 0,
+ ThreadIdRuleType,
+ PreviousModeRuleType,
+ NumberRuleType,
+ MaxRuleType
+} KPHSS_RULE_TYPE;
+
+/* Block types */
+
+#define KPHSS_BLOCK_SUCCESS(Status) (NT_SUCCESS(Status) && (Status) != STATUS_TIMEOUT)
+
+typedef enum _KPHSS_BLOCK_TYPE
+{
+ ResetBlockType,
+ EventBlockType,
+ ArgumentBlockType,
+ ProcessBlockType,
+ ModuleBlockType
+} KPHSS_BLOCK_TYPE;
+
+typedef struct _KPHSS_BLOCK_HEADER
+{
+ USHORT Size; /* a.k.a. NextEntryOffset */
+ USHORT Type;
+} KPHSS_BLOCK_HEADER, *PKPHSS_BLOCK_HEADER;
+
+typedef struct _KPHSS_RESET_BLOCK
+{
+ KPHSS_BLOCK_HEADER Header;
+} KPHSS_RESET_BLOCK, *PKPHSS_RESET_BLOCK;
+
+#define TAG_EVENT_BLOCK ('BEhP')
+
+#define KPHSS_EVENT_PROBE_ARGUMENTS_FAILED 0x00000001
+#define KPHSS_EVENT_COPY_ARGUMENTS_FAILED 0x00000002
+#define KPHSS_EVENT_KERNEL_MODE 0x00000004
+#define KPHSS_EVENT_USER_MODE 0x00000008
+
+typedef struct _KPHSS_EVENT_BLOCK
+{
+ KPHSS_BLOCK_HEADER Header;
+ USHORT Flags;
+ LARGE_INTEGER Time;
+ CLIENT_ID ClientId;
+
+ /* The system service number. */
+ ULONG Number;
+ /* The number of ULONG arguments to the system service. */
+ USHORT NumberOfArguments;
+ USHORT ArgumentsOffset; /* ULONG[] */
+
+ /* The number of PVOIDs in the trace. */
+ USHORT TraceCount;
+ USHORT TraceOffset; /* PVOID[] */
+} KPHSS_EVENT_BLOCK, *PKPHSS_EVENT_BLOCK;
+
+/* Argument Blocks
+ *
+ * These blocks provide additional information about
+ * arguments.
+ */
+
+#define TAG_ARGUMENT_BLOCK ('BAhP')
+
+#define KPHSS_ARGUMENT_BLOCK_OVERHEAD \
+ FIELD_OFFSET(KPHSS_ARGUMENT_BLOCK, Normal)
+#define KPHSS_ARGUMENT_BLOCK_SIZE(InnerSize) \
+ (KPHSS_ARGUMENT_BLOCK_OVERHEAD + (InnerSize))
+
+typedef struct _KPHSS_ARGUMENT_BLOCK
+{
+ KPHSS_BLOCK_HEADER Header;
+ UCHAR Index;
+ UCHAR Type; /* KPHSS_ARGUMENT_TYPE */
+
+ union
+ {
+ ULONG Normal;
+
+ LARGE_INTEGER Simple;
+ KPHSS_HANDLE Handle;
+ KPHSS_STRING String;
+ KPHSS_WSTRING WString;
+ KPHSS_ANSI_STRING AnsiString;
+ KPHSS_UNICODE_STRING UnicodeString;
+ KPHSS_OBJECT_ATTRIBUTES ObjectAttributes;
+ CLIENT_ID ClientId;
+ CONTEXT Context;
+ KPHSS_INITIAL_TEB InitialTeb;
+ GUID Guid;
+ };
+} KPHSS_ARGUMENT_BLOCK, *PKPHSS_ARGUMENT_BLOCK;
+
+/* Process Blocks
+ *
+ * These blocks notify the client of a new process.
+ */
+
+#define TAG_PROCESS_BLOCK ('BPhP')
+
+typedef struct _KPHSS_PROCESS_BLOCK
+{
+ KPHSS_BLOCK_HEADER Header;
+
+ HANDLE ProcessId;
+ USHORT NameOffset; /* KPHSS_WSTRING */
+ USHORT ImageFileNameOffset; /* KPHSS_WSTRING */
+} KPHSS_PROCESS_BLOCK, *PKPHSS_PROCESS_BLOCK;
+
+/* Module Blocks
+ *
+ * These blocks provide information about modules
+ * loaded by a process.
+ */
+
+#define TAG_MODULE_BLOCK ('BMhP')
+
+typedef struct _KPHSS_MODULE_BLOCK
+{
+ KPHSS_BLOCK_HEADER Header;
+
+ HANDLE ProcessId;
+ PVOID ModuleBase;
+ ULONG ModuleSize;
+ USHORT FileNameOffset; /* KPHSS_WSTRING */
+} KPHSS_MODULE_BLOCK, *PKPHSS_MODULE_BLOCK;
+
+/* Functions */
+
+NTSTATUS KphSsLogInit();
+NTSTATUS KphSsLogDeinit();
+NTSTATUS KphSsLogStart();
+NTSTATUS KphSsLogStop();
+
+NTSTATUS KphSsCreateClientEntry(
+ __out PKPHSS_CLIENT_ENTRY *ClientEntry,
+ __in HANDLE ProcessHandle,
+ __in HANDLE ReadSemaphoreHandle,
+ __in HANDLE WriteSemaphoreHandle,
+ __in PVOID BufferBase,
+ __in ULONG BufferSize,
+ __in KPROCESSOR_MODE AccessMode
+ );
+
+NTSTATUS KphSsEnableClientEntry(
+ __in PKPHSS_CLIENT_ENTRY ClientEntry,
+ __in BOOLEAN Enable
+ );
+
+NTSTATUS KphSsQueryClientEntry(
+ __in PKPHSS_CLIENT_ENTRY ClientEntry,
+ __out_bcount_opt(ClientInformationLength) PKPHSS_CLIENT_INFORMATION ClientInformation,
+ __in ULONG ClientInformationLength,
+ __out_opt PULONG ReturnLength,
+ __in KPROCESSOR_MODE AccessMode
+ );
+
+NTSTATUS KphSsCreateRuleSetEntry(
+ __out PKPHSS_RULESET_ENTRY *RuleSetEntry,
+ __in PKPHSS_CLIENT_ENTRY ClientEntry,
+ __in KPHSS_FILTER_TYPE DefaultFilterType,
+ __in KPHSS_RULESET_ACTION Action
+ );
+
+HANDLE KphSsGetHandleRule(
+ __in PKPHSS_RULE_ENTRY RuleEntry
+ );
+
+NTSTATUS KphSsRemoveRule(
+ __in PKPHSS_RULESET_ENTRY RuleSetEntry,
+ __in HANDLE RuleEntryHandle
+ );
+
+NTSTATUS KphSsAddProcessIdRule(
+ __out PKPHSS_RULE_ENTRY *RuleEntry,
+ __in PKPHSS_RULESET_ENTRY RuleSetEntry,
+ __in KPHSS_FILTER_TYPE FilterType,
+ __in HANDLE ProcessId
+ );
+
+NTSTATUS KphSsAddThreadIdRule(
+ __out PKPHSS_RULE_ENTRY *RuleEntry,
+ __in PKPHSS_RULESET_ENTRY RuleSetEntry,
+ __in KPHSS_FILTER_TYPE FilterType,
+ __in HANDLE ThreadId
+ );
+
+NTSTATUS KphSsAddPreviousModeRule(
+ __out PKPHSS_RULE_ENTRY *RuleEntry,
+ __in PKPHSS_RULESET_ENTRY RuleSetEntry,
+ __in KPHSS_FILTER_TYPE FilterType,
+ __in KPROCESSOR_MODE PreviousMode
+ );
+
+NTSTATUS KphSsAddNumberRule(
+ __out PKPHSS_RULE_ENTRY *RuleEntry,
+ __in PKPHSS_RULESET_ENTRY RuleSetEntry,
+ __in KPHSS_FILTER_TYPE FilterType,
+ __in ULONG Number
+ );
+
+#endif
diff --git a/branches/ph-plugins/KProcessHacker/include/sysservicedata.h b/branches/ph-plugins/KProcessHacker/include/sysservicedata.h
new file mode 100644
index 000000000..80f2e870b
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/include/sysservicedata.h
@@ -0,0 +1,166 @@
+/*
+ * Process Hacker Driver -
+ * system service logging (data)
+ *
+ * Copyright (C) 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 .
+ */
+
+#ifndef _SYSSERVICEDATA_H
+#define _SYSSERVICEDATA_H
+
+#include "kph.h"
+
+#define TAG_CALL_ENTRY ('cShP')
+
+typedef enum _KPHSS_ARGUMENT_TYPE
+{
+ /* Having argument info for out variables is very rare
+ * because usually the caller does not fill in anything
+ * in the variable. In some cases, however, the caller
+ * does specify a length (usually Length, or MaximumLength).
+ *
+ * Note that with the exception of a few types such as
+ * HANDLE, all types listed here are POINTER TYPES
+ * (although a handle is the size of a pointer). This
+ * is because non-pointer arguments are already recorded
+ * in the event block.
+ */
+
+ /* Anything passed by value */
+ NormalArgument = 0,
+
+ /* PBOOLEAN */
+ Int8Argument,
+ /* P(U)SHORT */
+ Int16Argument,
+ /* P(U)LONG */
+ Int32Argument,
+ /* P(U)LARGE_INTEGER */
+ Int64Argument,
+ /* HANDLE */
+ /* Only object manager handles, no fake handles. */
+ HandleArgument,
+ /* PSTR */
+ StringArgument,
+ /* PWSTR */
+ WStringArgument,
+ /* PANSI_STRING */
+ AnsiStringArgument,
+ /* PUNICODE_STRING */
+ UnicodeStringArgument,
+ /* POBJECT_ATTRIBUTES */
+ ObjectAttributesArgument,
+ /* PCLIENT_ID */
+ ClientIdArgument,
+ /* PCONTEXT */
+ ContextArgument,
+ /* PINITIAL_TEB */
+ InitialTebArgument,
+ /* PGUID */
+ GuidArgument
+} KPHSS_ARGUMENT_TYPE;
+
+typedef struct _KPHSS_HANDLE
+{
+ CLIENT_ID ClientId;
+ USHORT TypeNameOffset; /* KPHSS_WSTRING */
+ USHORT NameOffset; /* KPHSS_WSTRING */
+} KPHSS_HANDLE, *PKPHSS_HANDLE;
+
+typedef struct _KPHSS_STRING
+{
+ USHORT Length;
+ CHAR Buffer[1];
+} KPHSS_STRING, *PKPHSS_STRING;
+
+typedef struct _KPHSS_WSTRING
+{
+ USHORT Length;
+ WCHAR Buffer[1];
+} KPHSS_WSTRING, *PKPHSS_WSTRING;
+
+typedef struct _KPHSS_ANSI_STRING
+{
+ USHORT Length;
+ USHORT MaximumLength;
+ PSTR Pointer;
+ CHAR Buffer[1];
+} KPHSS_ANSI_STRING, *PKPHSS_ANSI_STRING;
+
+typedef struct _KPHSS_UNICODE_STRING
+{
+ USHORT Length;
+ USHORT MaximumLength;
+ PWSTR Pointer;
+ WCHAR Buffer[1];
+} KPHSS_UNICODE_STRING, *PKPHSS_UNICODE_STRING;
+
+typedef struct _KPHSS_OBJECT_ATTRIBUTES
+{
+ union
+ {
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ struct
+ {
+ ULONG Length;
+ HANDLE RootDirectory;
+ PUNICODE_STRING ObjectName;
+ ULONG Attributes;
+ PVOID SecurityDescriptor;
+ PVOID SecurityQualityOfService;
+ };
+ };
+
+ USHORT RootDirectoryOffset; /* KPHSS_HANDLE */
+ USHORT ObjectNameOffset; /* KPHSS_UNICODE_STRING */
+} KPHSS_OBJECT_ATTRIBUTES, *PKPHSS_OBJECT_ATTRIBUTES;
+
+typedef struct _KPHSS_INITIAL_TEB
+{
+ struct
+ {
+ PVOID OldStackBase;
+ PVOID OldStackLimit;
+ } OldInitialTeb;
+ PVOID StackBase;
+ PVOID StackLimit;
+ PVOID StackAllocationBase;
+} KPHSS_INITIAL_TEB, *PKPHSS_INITIAL_TEB;
+
+#ifndef _SYSSERVICEDATA_PRIVATE
+extern RTL_GENERIC_TABLE KphSsCallTable;
+#endif
+
+#define KPHSS_MAXIMUM_ARGUMENT_BLOCKS 20
+
+typedef struct _KPHSS_CALL_ENTRY
+{
+ PULONG Number;
+ PSTR Name;
+ ULONG NumberOfArguments;
+ KPHSS_ARGUMENT_TYPE Arguments[KPHSS_MAXIMUM_ARGUMENT_BLOCKS];
+} KPHSS_CALL_ENTRY, *PKPHSS_CALL_ENTRY;
+
+VOID KphSsDataInit();
+VOID KphSsDataDeinit();
+
+PKPHSS_CALL_ENTRY KphSsLookupCallEntry(
+ __in ULONG Number
+ );
+
+#endif
\ No newline at end of file
diff --git a/branches/ph-plugins/KProcessHacker/include/sysservicep.h b/branches/ph-plugins/KProcessHacker/include/sysservicep.h
new file mode 100644
index 000000000..50f297c33
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/include/sysservicep.h
@@ -0,0 +1,414 @@
+/*
+ * Process Hacker Driver -
+ * system service logging
+ *
+ * Copyright (C) 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 .
+ */
+
+#ifndef _SYSSERVICEP_H
+#define _SYSSERVICEP_H
+
+#define _SYSSERVICE_PRIVATE
+#include "sysservice.h"
+#include "ex.h"
+#include "ref.h"
+
+/* PKPHPSS_KIFASTCALLENTRYPROC
+ *
+ * Represents a function called by KphpSsNewKiFastCallEntry.
+ */
+typedef VOID (NTAPI *PKPHPSS_KIFASTCALLENTRYPROC)(
+ __in ULONG Number,
+ __in ULONG *Arguments,
+ __in ULONG NumberOfArguments,
+ __in PKSERVICE_TABLE_DESCRIPTOR ServiceTable,
+ __in PKTHREAD Thread
+ );
+
+/* Client entries
+ *
+ * Client entries describe a process and a circular buffer which
+ * receives logging events.
+ */
+
+typedef struct _KPHSS_CLIENT_ENTRY
+{
+ PEPROCESS Process;
+ BOOLEAN Enabled;
+
+ /* Buffer */
+ PKSEMAPHORE ReadSemaphore;
+ PKSEMAPHORE WriteSemaphore;
+ FAST_MUTEX BufferMutex;
+ PVOID BufferBase;
+ ULONG BufferSize;
+ ULONG BufferCursor;
+
+ /* Statistics */
+ ULONG NumberOfBlocksWritten; /* excludes reset blocks */
+ ULONG NumberOfBlocksDropped;
+} KPHSS_CLIENT_ENTRY, *PKPHSS_CLIENT_ENTRY;
+
+/* Rulesets
+ *
+ * Rulesets contain a list of rules and an action to take if a
+ * system service matches the set of rules.
+ */
+
+#define KPHSS_RULESET_ENTRY(ListEntry) \
+ CONTAINING_RECORD((ListEntry), KPHSS_RULESET_ENTRY, RuleSetListEntry)
+#define KPHSS_RULESET_ENTRY_LIMIT 10
+#define KPHSS_RULE_HANDLE_INCREMENT 4
+
+typedef struct _KPHSS_RULESET_ENTRY
+{
+ LIST_ENTRY RuleSetListEntry;
+ /* The client is referenced. */
+ PKPHSS_CLIENT_ENTRY Client;
+
+ KPHSS_RULESET_ACTION Action;
+ KPHSS_FILTER_TYPE DefaultFilterType;
+
+ ULONG NextRuleHandle;
+ EX_PUSH_LOCK RuleListPushLock;
+ /* A list of rules. Each rule is referenced when stored. */
+ LIST_ENTRY RuleListHead;
+} KPHSS_RULESET_ENTRY, *PKPHSS_RULESET_ENTRY;
+
+/* Rules */
+
+#define KPHSS_RULE_ENTRY(ListEntry) \
+ CONTAINING_RECORD((ListEntry), KPHSS_RULE_ENTRY, RuleListEntry)
+
+typedef struct _KPHSS_RULE_ENTRY
+{
+ BOOLEAN Initialized;
+ HANDLE Handle;
+ LIST_ENTRY RuleListEntry;
+
+ KPHSS_FILTER_TYPE FilterType;
+ KPHSS_RULE_TYPE RuleType;
+
+ union
+ {
+ struct
+ {
+ HANDLE ProcessId;
+ } ProcessIdRule;
+ struct
+ {
+ HANDLE ThreadId;
+ } ThreadIdRule;
+ struct
+ {
+ KPROCESSOR_MODE PreviousMode;
+ } PreviousModeRule;
+ struct
+ {
+ ULONG Number;
+ } NumberRule;
+ };
+} KPHSS_RULE_ENTRY, *PKPHSS_RULE_ENTRY;
+
+typedef enum _KPHSS_SEQUENCE_MODE
+{
+ NoSequence,
+ StartSequence,
+ InSequence,
+ EndSequence
+} KPHSS_SEQUENCE_MODE;
+
+#define TAG_CAPTURE_TEMP_BUFFER ('tChP')
+#define CAPTURE_HANDLE_BUFFER_SIZE 0x400
+#define CAPTURE_UNICODE_STRING_MAX_SIZE 0x400
+
+/* KphpSsMatchRuleSetEntry
+ *
+ * Determines if a ruleset is relevant to an event.
+ *
+ * Note: This function is inlined for performance reasons.
+ */
+FORCEINLINE BOOLEAN KphpSsMatchRuleSetEntry(
+ __in PKPHSS_RULESET_ENTRY RuleSetEntry,
+ __in ULONG Number,
+ __in ULONG *Arguments,
+ __in ULONG NumberOfArguments,
+ __in PKSERVICE_TABLE_DESCRIPTOR ServiceTable,
+ __in PKTHREAD Thread,
+ __in KPROCESSOR_MODE PreviousMode
+ )
+{
+ PLIST_ENTRY currentListEntry;
+ ULONG i;
+ BOOLEAN ruleTypeUsedArray[MaxRuleType];
+ BOOLEAN ruleTypeIncludeArray[MaxRuleType];
+ BOOLEAN ruleTypeExcludeArray[MaxRuleType];
+ BOOLEAN ruleTypeFailedArray[MaxRuleType];
+ BOOLEAN isRuleSetMatch;
+
+ /* Due to the lack of proper boolean expression support,
+ * we are going to have these rules:
+ *
+ * * Each rule type has four arrays. The standard
+ * filtering rules apply to each rule type,
+ * except that on an include we increment the value
+ * in the include array and on an exclude we
+ * increment the value in the exclude array. On a
+ * failed include we increment the value in the
+ * failed array.
+ * * When we're done matching the rules, we'll look
+ * at the default filter type. If it's Include,
+ * we assume the ruleset matches. If it's Exclude,
+ * we assume the ruleset fails.
+ * * We will go through each rule type and look at
+ * the two arrays. See the code for further
+ * information.
+ */
+
+ /* Initialize the arrays. */
+ for (i = 0; i < MaxRuleType; i++)
+ {
+ ruleTypeUsedArray[i] = FALSE;
+ ruleTypeIncludeArray[i] = FALSE;
+ ruleTypeExcludeArray[i] = FALSE;
+ ruleTypeFailedArray[i] = FALSE;
+ }
+
+ KeEnterCriticalRegion();
+ ExAcquirePushLockShared(&RuleSetEntry->RuleListPushLock);
+
+ currentListEntry = RuleSetEntry->RuleListHead.Flink;
+
+ while (currentListEntry != &RuleSetEntry->RuleListHead)
+ {
+ PKPHSS_RULE_ENTRY ruleEntry = KPHSS_RULE_ENTRY(currentListEntry);
+ BOOLEAN isRuleMatch = FALSE;
+
+ /* Check if the rule is initialized, and if
+ * the rule type has already been failed -
+ * Exclude filter types take precedence.
+ */
+ if (
+ !ruleEntry->Initialized ||
+ ruleTypeExcludeArray[ruleEntry->RuleType]
+ )
+ {
+ currentListEntry = currentListEntry->Flink;
+ continue;
+ }
+
+ /* Attempt to match the rule. All rule types are
+ * considered in this one function.
+ */
+ switch (ruleEntry->RuleType)
+ {
+ case ProcessIdRuleType:
+ if (PsGetProcessId(IoThreadToProcess(Thread)) ==
+ ruleEntry->ProcessIdRule.ProcessId)
+ isRuleMatch = TRUE;
+ break;
+ case ThreadIdRuleType:
+ if (PsGetThreadId(Thread) == ruleEntry->ThreadIdRule.ThreadId)
+ isRuleMatch = TRUE;
+ break;
+ case PreviousModeRuleType:
+ if (PreviousMode == ruleEntry->PreviousModeRule.PreviousMode)
+ isRuleMatch = TRUE;
+ break;
+ case NumberRuleType:
+ if (Number == ruleEntry->NumberRule.Number)
+ isRuleMatch = TRUE;
+ break;
+ }
+
+ /* Now that we have attempted to match the rule, we
+ * must look at the rule filter type to determine
+ * what to do.
+ */
+ if (isRuleMatch)
+ {
+ if (ruleEntry->FilterType == IncludeFilterType)
+ {
+ ruleTypeIncludeArray[ruleEntry->RuleType] = TRUE;
+ }
+ else if (ruleEntry->FilterType == ExcludeFilterType)
+ {
+ ruleTypeExcludeArray[ruleEntry->RuleType] = TRUE;
+ }
+ }
+ else
+ {
+ if (ruleEntry->FilterType == IncludeFilterType)
+ {
+ ruleTypeFailedArray[ruleEntry->RuleType] = TRUE;
+ }
+ }
+
+ /* Declare that we have used the rule type. */
+ ruleTypeUsedArray[ruleEntry->RuleType] = TRUE;
+
+ currentListEntry = currentListEntry->Flink;
+ }
+
+ ExReleasePushLock(&RuleSetEntry->RuleListPushLock);
+ KeLeaveCriticalRegion();
+
+ /* Look at the default filter type. If it's Include,
+ * we assume the ruleset matches. Otherwise, we
+ * assume it fails.
+ */
+ if (RuleSetEntry->DefaultFilterType == IncludeFilterType)
+ {
+ isRuleSetMatch = TRUE;
+ }
+ else if (RuleSetEntry->DefaultFilterType == ExcludeFilterType)
+ {
+ isRuleSetMatch = FALSE;
+ }
+
+ /* Go through the rule type match/failed arrays. */
+
+ for (i = 0; i < MaxRuleType; i++)
+ {
+ /* Make sure this rule type has been used. */
+ if (!ruleTypeUsedArray[i])
+ continue;
+
+ /* The ordering of these if statements are
+ * extremely important. The order of precedence
+ * is: exclude, include, failed include. Failed include
+ * doesn't apply if we're using the Include default
+ * filter type, though.
+ */
+ if (ruleTypeExcludeArray[i])
+ {
+ isRuleSetMatch = FALSE;
+ break;
+ }
+ else if (ruleTypeIncludeArray[i])
+ {
+ isRuleSetMatch = TRUE;
+ }
+ else if (
+ ruleTypeFailedArray[i] &&
+ RuleSetEntry->DefaultFilterType != IncludeFilterType
+ )
+ {
+ isRuleSetMatch = FALSE;
+ break;
+ }
+ }
+
+ return isRuleSetMatch;
+}
+
+/* Functions */
+
+VOID NTAPI KphpSsClientEntryDeleteProcedure(
+ __in PVOID Object,
+ __in ULONG Flags
+ );
+
+VOID NTAPI KphpSsRuleSetEntryDeleteProcedure(
+ __in PVOID Object,
+ __in ULONG Flags
+ );
+
+NTSTATUS KphpSsAddRule(
+ __out PKPHSS_RULE_ENTRY *RuleEntry,
+ __in PKPHSS_RULESET_ENTRY RuleSetEntry,
+ __in KPHSS_FILTER_TYPE FilterType,
+ __in KPHSS_RULE_TYPE RuleType
+ );
+
+NTSTATUS KphpSsCreateEventBlock(
+ __out PKPHSS_EVENT_BLOCK *EventBlock,
+ __in PKTHREAD Thread,
+ __in ULONG Number,
+ __in ULONG *Arguments,
+ __in ULONG NumberOfArguments
+ );
+
+VOID KphpSsFreeEventBlock(
+ __in PKPHSS_EVENT_BLOCK EventBlock
+ );
+
+NTSTATUS KphpSsCaptureSimpleArgument(
+ __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock,
+ __in PVOID Argument,
+ __in KPHSS_ARGUMENT_TYPE Type,
+ __in KPROCESSOR_MODE PreviousMode
+ );
+
+NTSTATUS KphpSsCaptureHandleArgument(
+ __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock,
+ __in HANDLE Argument,
+ __in KPROCESSOR_MODE PreviousMode
+ );
+
+NTSTATUS KphpSsCaptureUnicodeStringArgument(
+ __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock,
+ __in PUNICODE_STRING Argument,
+ __in KPROCESSOR_MODE PreviousMode
+ );
+
+NTSTATUS KphpSsCaptureObjectAttributesArgument(
+ __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock,
+ __in POBJECT_ATTRIBUTES Argument,
+ __in KPROCESSOR_MODE PreviousMode
+ );
+
+NTSTATUS KphpSsCaptureClientIdArgument(
+ __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock,
+ __in PCLIENT_ID Argument,
+ __in KPROCESSOR_MODE PreviousMode
+ );
+
+NTSTATUS KphpSsCreateArgumentBlock(
+ __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock,
+ __in ULONG Number,
+ __in ULONG Argument,
+ __in ULONG Index
+ );
+
+PKPHSS_ARGUMENT_BLOCK KphpSsAllocateArgumentBlock(
+ __in ULONG InnerSize,
+ __in KPHSS_ARGUMENT_TYPE Type
+ );
+
+VOID KphpSsFreeArgumentBlock(
+ __in PKPHSS_ARGUMENT_BLOCK ArgumentBlock
+ );
+
+NTSTATUS KphpSsWriteBlock(
+ __in PKPHSS_CLIENT_ENTRY ClientEntry,
+ __in_opt PKPHSS_BLOCK_HEADER Block,
+ __in KPHSS_SEQUENCE_MODE SequenceMode
+ );
+
+VOID NTAPI KphpSsLogSystemServiceCall(
+ __in ULONG Number,
+ __in ULONG *Arguments,
+ __in ULONG NumberOfArguments,
+ __in PKSERVICE_TABLE_DESCRIPTOR ServiceTable,
+ __in PKTHREAD Thread
+ );
+
+VOID NTAPI KphpSsNewKiFastCallEntry();
+
+#endif
diff --git a/branches/ph-plugins/KProcessHacker/include/test.h b/branches/ph-plugins/KProcessHacker/include/test.h
new file mode 100644
index 000000000..49dc96095
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/include/test.h
@@ -0,0 +1,30 @@
+/*
+ * Process Hacker Driver -
+ * testing code
+ *
+ * Copyright (C) 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 .
+ */
+
+#ifndef _TEST_H
+#define _TEST_H
+
+#include "kph.h"
+
+VOID KphTestPushLock();
+
+#endif
diff --git a/branches/ph-plugins/KProcessHacker/include/trace.h b/branches/ph-plugins/KProcessHacker/include/trace.h
new file mode 100644
index 000000000..6b707ede5
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/include/trace.h
@@ -0,0 +1,188 @@
+/*
+ * Process Hacker Driver -
+ * stack tracing
+ *
+ * Copyright (C) 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 .
+ */
+
+#ifndef _TRACE_H
+#define _TRACE_H
+
+#include "types.h"
+
+/* Stack Tracing */
+
+/* Sensible limit that may or may not correspond to the actual Windows value. */
+#define MAX_STACK_DEPTH 64
+
+#define RTL_WALK_USER_MODE_STACK 0x00000001
+#define RTL_WALK_VALID_FLAGS 0x00000001
+
+/* RtlWalkFrameChain
+ *
+ * Walks an EBP chain and fills out an array of addresses.
+ *
+ * Return value: the number of frames found.
+ */
+NTSYSAPI ULONG NTAPI RtlWalkFrameChain(
+ __out PVOID *Callers,
+ __in ULONG Count,
+ __in ULONG Flags
+ );
+
+/* Trace Database */
+
+#define RTL_TRACE_IN_USER_MODE 0x00000001
+#define RTL_TRACE_IN_KERNEL_MODE 0x00000002
+#define RTL_TRACE_USE_NONPAGED_POOL 0x00000004
+#define RTL_TRACE_USE_PAGED_POOL 0x00000008
+
+typedef struct _RTL_TRACE_BLOCK
+{
+ ULONG Magic;
+ ULONG Count; /* Reference count */
+ ULONG Size; /* Size, in PVOIDs, of the trace */
+
+ SIZE_T UserCount;
+ SIZE_T UserSize;
+ PVOID UserContext;
+
+ struct _RTL_TRACE_BLOCK *Next;
+ PVOID *Trace;
+} RTL_TRACE_BLOCK, *PRTL_TRACE_BLOCK;
+
+typedef struct _RTL_TRACE_DATABASE *PRTL_TRACE_DATABASE;
+
+/* Enumeration context. */
+typedef struct _RTL_TRACE_ENUMERATE
+{
+ PRTL_TRACE_DATABASE Database;
+ ULONG Index;
+ PRTL_TRACE_BLOCK Block;
+} RTL_TRACE_ENUMERATE, *PRTL_TRACE_ENUMERATE;
+
+typedef ULONG (*RTL_TRACE_HASH_FUNCTION)(
+ ULONG Count,
+ PVOID *Trace
+ );
+
+PRTL_TRACE_DATABASE RtlTraceDatabaseCreate(
+ __in ULONG Buckets,
+ __in_opt SIZE_T MaximumSize,
+ __in ULONG Flags, /* optional in user-mode */
+ __in ULONG Tag, /* optional in user-mode */
+ __in_opt RTL_TRACE_HASH_FUNCTION HashFunction
+ );
+
+BOOLEAN RtlTraceDatabaseDestroy(
+ __in PRTL_TRACE_DATABASE Database
+ );
+
+BOOLEAN RtlTraceDatabaseValidate(
+ __in PRTL_TRACE_DATABASE Database
+ );
+
+BOOLEAN RtlTraceDatabaseAdd(
+ __in PRTL_TRACE_DATABASE Database,
+ __in ULONG Count,
+ __in PVOID *Trace,
+ __out_opt PRTL_TRACE_BLOCK *TraceBlock
+ );
+
+/* RtlTraceDatabaseEnumerate
+ *
+ * Enumerates the trace blocks in the specified trace database.
+ *
+ * Database: The trace database to process.
+ * Enumerate: A context structure for the enumeration. Zero the
+ * structure if you are using it for the first time.
+ * TraceBlock: The trace block that was found by the function.
+ *
+ * Return value: TRUE if a trace block was found, FALSE if there
+ * are no more trace blocks.
+ */
+BOOLEAN RtlTraceDatabaseEnumerate(
+ __in PRTL_TRACE_DATABASE Database,
+ __inout PRTL_TRACE_ENUMERATE Enumerate,
+ __out PRTL_TRACE_BLOCK *TraceBlock
+ );
+
+BOOLEAN RtlTraceDatabaseFind(
+ __in PRTL_TRACE_DATABASE Database,
+ __in ULONG Count,
+ __in PVOID *Trace,
+ __out_opt PRTL_TRACE_BLOCK *TraceBlock
+ );
+
+/* Note: locking/unlocking is only needed when trace blocks are modified.
+ * It is not needed for adding/enumerating/finding. */
+VOID RtlTraceDatabaseLock(
+ __in PRTL_TRACE_DATABASE Database
+ );
+
+VOID RtlTraceDatabaseUnlock(
+ __in PRTL_TRACE_DATABASE Database
+ );
+
+/* KPH trace interface */
+
+typedef enum _KPH_CAPTURE_AND_ADD_STACK_TYPE
+{
+ KphCaptureAndAddKModeStack,
+ KphCaptureAndAddUModeStack,
+ KphCaptureAndAddBothStacks,
+ KphCaptureAndAddMaximum
+} KPH_CAPTURE_AND_ADD_STACK_TYPE, *PKPH_CAPTURE_AND_ADD_STACK_TYPE;
+
+typedef struct _KPH_TRACE_DATABASE
+{
+ PRTL_TRACE_DATABASE Database;
+} KPH_TRACE_DATABASE, *PKPH_TRACE_DATABASE;
+
+typedef struct _KPH_TRACEDB_INFORMATION
+{
+ ULONG NextEntryOffset;
+ ULONG Count;
+ ULONG TraceSize;
+ PVOID Trace[1];
+} KPH_TRACEDB_INFORMATION, *PKPH_TRACEDB_INFORMATION;
+
+NTSTATUS KphTraceDatabaseInitialization();
+
+BOOLEAN KphCaptureAndAddStack(
+ __in PKPH_TRACE_DATABASE Database,
+ __in KPH_CAPTURE_AND_ADD_STACK_TYPE Type,
+ __out_opt PRTL_TRACE_BLOCK *TraceBlock
+ );
+
+ULONG KphCaptureStackBackTrace(
+ __in ULONG FramesToSkip,
+ __in ULONG FramesToCapture,
+ __in_opt ULONG Flags,
+ __out_ecount(FramesToCapture) PVOID *BackTrace,
+ __out_opt PULONG BackTraceHash
+ );
+
+NTSTATUS KphCreateTraceDatabase(
+ __out PKPH_TRACE_DATABASE *Database,
+ __in_opt SIZE_T MaximumSize,
+ __in ULONG Flags,
+ __in ULONG Tag
+ );
+
+#endif
diff --git a/branches/ph-plugins/KProcessHacker/include/types.h b/branches/ph-plugins/KProcessHacker/include/types.h
new file mode 100644
index 000000000..e1ca291c1
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/include/types.h
@@ -0,0 +1,7 @@
+#ifndef _TYPES_H
+#define _TYPES_H
+
+#include
+#include "version.h"
+
+#endif
diff --git a/branches/ph-plugins/KProcessHacker/include/util.h b/branches/ph-plugins/KProcessHacker/include/util.h
new file mode 100644
index 000000000..b62b0d22e
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/include/util.h
@@ -0,0 +1,133 @@
+/*
+ * Process Hacker Driver -
+ * utility functions
+ *
+ * Copyright (C) 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 .
+ */
+
+#ifndef _UTIL_H
+#define _UTIL_H
+
+#include "kph.h"
+
+/* Streams
+ *
+ * Streams are small buffer management structures. They
+ * automatically raise an exception if the buffer is overrun.
+ */
+
+typedef struct _KPH_STREAM
+{
+ PVOID Buffer;
+ ULONG Length;
+ ULONG Position;
+} KPH_STREAM, *PKPH_STREAM;
+
+typedef enum _KPH_STREAM_ORIGIN
+{
+ StartOrigin,
+ CurrentOrigin,
+ EndOrigin
+} KPH_STREAM_ORIGIN;
+
+VOID KphInitializeStream(
+ __out PKPH_STREAM Stream,
+ __in PVOID Buffer,
+ __in ULONG Length
+ );
+
+ULONG KphWriteDataStream(
+ __inout PKPH_STREAM Stream,
+ __in PVOID Data,
+ __in ULONG Length
+ );
+
+/* KphCheckStreamPosition
+ *
+ * Checks a stream position and raises an exception if
+ * appropriate.
+ */
+FORCEINLINE VOID KphCheckStreamPosition(
+ __in PKPH_STREAM Stream,
+ __in ULONG Position
+ )
+{
+ if (Position > Stream->Length)
+ ExRaiseStatus(STATUS_BUFFER_TOO_SMALL);
+}
+
+/* KphPositionStream
+ *
+ * Gets the current position of the specified stream.
+ */
+FORCEINLINE ULONG KphPositionStream(
+ __in PKPH_STREAM Stream
+ )
+{
+ return Stream->Position;
+}
+
+/* KphWriteInt8Stream
+ *
+ * Writes a 1-byte value to a stream.
+ */
+FORCEINLINE VOID KphWriteInt8Stream(
+ __inout PKPH_STREAM Stream,
+ __in BOOLEAN Value
+ )
+{
+ KphWriteDataStream(Stream, &Value, sizeof(BOOLEAN));
+}
+
+/* KphWriteInt16Stream
+ *
+ * Writes a 2-byte value to a stream.
+ */
+FORCEINLINE VOID KphWriteInt16Stream(
+ __inout PKPH_STREAM Stream,
+ __in SHORT Value
+ )
+{
+ KphWriteDataStream(Stream, &Value, sizeof(SHORT));
+}
+
+/* KphWriteInt32Stream
+ *
+ * Writes a 4-byte value to a stream.
+ */
+FORCEINLINE VOID KphWriteInt32Stream(
+ __inout PKPH_STREAM Stream,
+ __in LONG Value
+ )
+{
+ KphWriteDataStream(Stream, &Value, sizeof(LONG));
+}
+
+/* KphWriteInt64Stream
+ *
+ * Writes a 8-byte value to a stream.
+ */
+FORCEINLINE VOID KphWriteInt64Stream(
+ __inout PKPH_STREAM Stream,
+ __in PLARGE_INTEGER Value
+ )
+{
+ KphWriteDataStream(Stream, Value, sizeof(LARGE_INTEGER));
+}
+
+#endif
diff --git a/branches/ph-plugins/KProcessHacker/include/version.h b/branches/ph-plugins/KProcessHacker/include/version.h
new file mode 100644
index 000000000..00d6ba5ab
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/include/version.h
@@ -0,0 +1,241 @@
+/*
+ * Process Hacker Driver -
+ * Windows version-specific data
+ *
+ * Copyright (C) 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 .
+ */
+
+#ifndef _VERSION_H
+#define _VERSION_H
+
+#include "kph.h"
+
+#define WINDOWS_XP 51
+#define WINDOWS_SERVER_2003 52
+#define WINDOWS_VISTA 60
+#define WINDOWS_7 61
+
+#define KVOFF(object, offset) ((PCHAR)(object) + offset)
+#define SCAN_LENGTH 0x100000
+#define INIT_SCAN(scan, bytes, length, address, scanLength, displacement) \
+ ( \
+ ((scan).Initialized = TRUE), \
+ ((scan).Bytes = (bytes)), \
+ ((scan).Length = (length)), \
+ ((scan).StartAddress = (address)), \
+ ((scan).ScanLength = (scanLength)), \
+ ((scan).Displacement = (displacement)), \
+ bytes \
+ )
+
+typedef struct _KV_SCANPROC
+{
+ BOOLEAN Initialized;
+ PUCHAR Bytes;
+ ULONG Length;
+ ULONG_PTR StartAddress;
+ ULONG ScanLength;
+ LONG Displacement;
+} KV_SCANPROC, *PKV_SCANPROC;
+
+NTSTATUS KvInit();
+
+PVOID KvScanProc(
+ PKV_SCANPROC ScanProc
+ );
+
+PVOID KvVerifyPrologue(
+ PVOID Address
+ );
+
+#ifdef EXT
+#undef EXT
+#endif
+
+#ifdef _VERSION_PRIVATE
+#define EXT
+#define SCANNULL = { FALSE, NULL, 0, 0, 0, 0 }
+#else
+#define EXT extern
+#define SCANNULL
+#endif
+
+EXT ULONG WindowsVersion;
+EXT RTL_OSVERSIONINFOEXW RtlWindowsVersion;
+EXT ACCESS_MASK ProcessAllAccess;
+EXT ACCESS_MASK ThreadAllAccess;
+
+/* Offsets */
+/* Structures
+ * Et: ETHREAD
+ * Ep: EPROCESS
+ * Ot: OBJECT_TYPE
+ * Oti: OBJECT_TYPE_INITIALIZER, offset measured from an OBJECT_TYPE
+ */
+EXT ULONG OffEtClientId;
+EXT ULONG OffEtSpareByteForSs;
+EXT ULONG OffEtStartAddress;
+EXT ULONG OffEtWin32StartAddress;
+EXT ULONG OffEpJob;
+EXT ULONG OffEpObjectTable;
+EXT ULONG OffEpProtectedProcessOff;
+EXT ULONG OffEpProtectedProcessBit;
+EXT ULONG OffEpRundownProtect;
+EXT ULONG OffOhBody;
+EXT ULONG OffOtName;
+EXT ULONG OffOtiGenericMapping;
+EXT ULONG OffOtiOpenProcedure;
+
+/* Functions
+ */
+EXT KV_SCANPROC KiFastCallEntryScan SCANNULL;
+EXT KV_SCANPROC PsExitSpecialApcScan SCANNULL;
+EXT KV_SCANPROC PsTerminateProcessScan SCANNULL;
+EXT KV_SCANPROC PspTerminateThreadByPointerScan SCANNULL;
+
+/* System Call Numbers
+ */
+EXT ULONG SsNtAddAtom;
+EXT ULONG SsNtAlertResumeThread;
+EXT ULONG SsNtAlertThread;
+EXT ULONG SsNtAllocateLocallyUniqueId;
+EXT ULONG SsNtAllocateUserPhysicalPages;
+EXT ULONG SsNtAllocateUuids;
+EXT ULONG SsNtAllocateVirtualMemory;
+EXT ULONG SsNtApphelpCacheControl;
+EXT ULONG SsNtAreMappedFilesTheSame;
+EXT ULONG SsNtAssignProcessToJobObject;
+EXT ULONG SsNtCallbackReturn;
+EXT ULONG SsNtCancelDeviceWakeupRequest;
+EXT ULONG SsNtCancelIoFile;
+EXT ULONG SsNtCancelTimer;
+EXT ULONG SsNtClearEvent;
+EXT ULONG SsNtClose;
+EXT ULONG SsNtContinue;
+EXT ULONG SsNtCreateDebugObject;
+EXT ULONG SsNtCreateDirectoryObject;
+EXT ULONG SsNtCreateEvent;
+EXT ULONG SsNtCreateEventPair;
+EXT ULONG SsNtCreateFile;
+EXT ULONG SsNtCreateIoCompletion;
+EXT ULONG SsNtCreateJobObject;
+EXT ULONG SsNtCreateJobSet;
+EXT ULONG SsNtCreateKey;
+EXT ULONG SsNtCreateKeyedEvent;
+EXT ULONG SsNtCreateMailslotFile;
+EXT ULONG SsNtCreateMutant;
+EXT ULONG SsNtCreateNamedPipeFile;
+EXT ULONG SsNtCreatePagingFile;
+EXT ULONG SsNtCreatePort;
+EXT ULONG SsNtCreatePrivateNamespace;
+EXT ULONG SsNtCreateProcess;
+EXT ULONG SsNtCreateProcessEx;
+EXT ULONG SsNtCreateProfile;
+EXT ULONG SsNtCreateSection;
+EXT ULONG SsNtCreateSemaphore;
+EXT ULONG SsNtCreateSymbolicLinkObject;
+EXT ULONG SsNtCreateThread;
+EXT ULONG SsNtCreateTimer;
+EXT ULONG SsNtCreateToken;
+EXT ULONG SsNtCreateUserProcess;
+EXT ULONG SsNtCreateWaitablePort;
+EXT ULONG SsNtDebugActiveProcess;
+EXT ULONG SsNtDebugContinue;
+EXT ULONG SsNtDelayExecution;
+EXT ULONG SsNtDeleteAtom;
+EXT ULONG SsNtDeleteBootEntry;
+EXT ULONG SsNtDeleteDriverEntry;
+EXT ULONG SsNtDeleteFile;
+EXT ULONG SsNtDeleteKey;
+EXT ULONG SsNtDeleteObjectAuditAlarm;
+EXT ULONG SsNtDeletePrivateNamespace;
+EXT ULONG SsNtDeleteValueKey;
+EXT ULONG SsNtDeviceIoControlFile;
+EXT ULONG SsNtDisplayString;
+EXT ULONG SsNtDuplicateObject;
+EXT ULONG SsNtDuplicateToken;
+EXT ULONG SsNtEnumerateBootEntries;
+EXT ULONG SsNtEnumerateDriverEntries;
+EXT ULONG SsNtEnumerateKey;
+EXT ULONG SsNtEnumerateSystemEnvironmentValuesEx;
+EXT ULONG SsNtEnumerateValueKey;
+EXT ULONG SsNtExtendSection;
+EXT ULONG SsNtFilterToken;
+EXT ULONG SsNtFindAtom;
+EXT ULONG SsNtFlushBuffersFile;
+EXT ULONG SsNtFlushInstructionCache;
+EXT ULONG SsNtFlushKey;
+EXT ULONG SsNtFlushProcessWriteBuffers;
+EXT ULONG SsNtFlushVirtualMemory;
+EXT ULONG SsNtFlushWriteBuffer;
+EXT ULONG SsNtFreeUserPhysicalPages;
+EXT ULONG SsNtFreeVirtualMemory;
+EXT ULONG SsNtFsControlFile;
+EXT ULONG SsNtGetContextThread;
+EXT ULONG SsNtGetCurrentProcessorNumber;
+EXT ULONG SsNtGetDevicePowerState;
+EXT ULONG SsNtGetNextProcess;
+EXT ULONG SsNtGetNextThread;
+EXT ULONG SsNtGetPlugPlayEvent;
+EXT ULONG SsNtGetWriteWatch;
+EXT ULONG SsNtImpersonateAnonymousToken;
+EXT ULONG SsNtImpersonateClientOfPort;
+EXT ULONG SsNtImpersonateThread;
+EXT ULONG SsNtInitiatePowerAction;
+EXT ULONG SsNtIsProcessInJob;
+EXT ULONG SsNtIsSystemResumeAutomatic;
+EXT ULONG SsNtListenPort;
+EXT ULONG SsNtLoadDriver;
+EXT ULONG SsNtLoadKey;
+EXT ULONG SsNtLoadKey2;
+EXT ULONG SsNtLockFile;
+EXT ULONG SsNtLockVirtualMemory;
+EXT ULONG SsNtMakePermanentObject;
+EXT ULONG SsNtMakeTemporaryObject;
+EXT ULONG SsNtMapUserPhysicalPages;
+EXT ULONG SsNtMapUserPhysicalPagesScatter;
+EXT ULONG SsNtMapViewOfSection;
+EXT ULONG SsNtModifyBootEntry;
+EXT ULONG SsNtModifyDriverEntry;
+EXT ULONG SsNtNotifyChangeDirectoryFile;
+EXT ULONG SsNtNotifyChangeKey;
+EXT ULONG SsNtNotifyChangeMultipleKeys;
+EXT ULONG SsNtOpenDirectoryObject;
+EXT ULONG SsNtOpenEvent;
+EXT ULONG SsNtOpenEventPair;
+EXT ULONG SsNtOpenFile;
+EXT ULONG SsNtOpenIoCompletion;
+EXT ULONG SsNtOpenJobObject;
+EXT ULONG SsNtOpenKey;
+EXT ULONG SsNtOpenKeyedEvent;
+EXT ULONG SsNtOpenMutant;
+EXT ULONG SsNtOpenObjectAuditAlarm;
+EXT ULONG SsNtOpenProcess;
+EXT ULONG SsNtOpenProcessToken;
+EXT ULONG SsNtOpenProcessTokenEx;
+EXT ULONG SsNtOpenSection;
+EXT ULONG SsNtOpenSemaphore;
+EXT ULONG SsNtOpenSymbolicLinkObject;
+EXT ULONG SsNtOpenThread;
+EXT ULONG SsNtOpenThreadToken;
+EXT ULONG SsNtOpenThreadTokenEx;
+EXT ULONG SsNtOpenTimer;
+EXT ULONG SsNtReadFile;
+EXT ULONG SsNtWriteFile;
+
+#endif
diff --git a/branches/ph-plugins/KProcessHacker/include/zw.h b/branches/ph-plugins/KProcessHacker/include/zw.h
new file mode 100644
index 000000000..e6ab70f4e
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/include/zw.h
@@ -0,0 +1,45 @@
+/*
+ * Process Hacker Driver -
+ * system calls
+ *
+ * Copyright (C) 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 .
+ */
+
+#ifndef _ZW_H
+#define _ZW_H
+
+#include "types.h"
+
+NTSTATUS NTAPI ZwOpenProcessToken(
+ HANDLE ProcessHandle,
+ ACCESS_MASK DesiredAccess,
+ PHANDLE TokenHandle
+ );
+
+NTSTATUS NTAPI ZwSetInformationProcess(
+ HANDLE ProcessHandle,
+ PROCESSINFOCLASS ProcessInformationClass,
+ PVOID ProcessInformation,
+ ULONG ProcessInformationLength
+ );
+
+typedef NTSTATUS (NTAPI *_NtClose)(
+ HANDLE Handle
+ );
+
+#endif
diff --git a/branches/ph-plugins/KProcessHacker/io.c b/branches/ph-plugins/KProcessHacker/io.c
new file mode 100644
index 000000000..ddd8a779d
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/io.c
@@ -0,0 +1,284 @@
+/*
+ * Process Hacker Driver -
+ * I/O manager
+ *
+ * Copyright (C) 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 .
+ */
+
+#include "include/io.h"
+
+VOID KphpCopyInfoUnicodeString(
+ __out PVOID Information,
+ __in PUNICODE_STRING UnicodeString
+ );
+
+/* KphOpenDevice
+ *
+ * Opens a device object.
+ */
+NTSTATUS KphOpenDevice(
+ __out PHANDLE DeviceHandle,
+ __in POBJECT_ATTRIBUTES ObjectAttributes,
+ __in KPROCESSOR_MODE AccessMode
+ )
+{
+ return KphOpenNamedObject(
+ DeviceHandle,
+ 0,
+ ObjectAttributes,
+ *IoDeviceObjectType,
+ AccessMode
+ );
+}
+
+/* KphOpenDriver
+ *
+ * Opens a driver object.
+ */
+NTSTATUS KphOpenDriver(
+ __out PHANDLE DriverHandle,
+ __in POBJECT_ATTRIBUTES ObjectAttributes,
+ __in KPROCESSOR_MODE AccessMode
+ )
+{
+ return KphOpenNamedObject(
+ DriverHandle,
+ 0,
+ ObjectAttributes,
+ *IoDriverObjectType,
+ AccessMode
+ );
+}
+
+/* KphQueryInformationDriver
+ *
+ * Queries information about a driver object.
+ */
+NTSTATUS KphQueryInformationDriver(
+ __in HANDLE DriverHandle,
+ __in DRIVER_INFORMATION_CLASS DriverInformationClass,
+ __out_bcount_opt(DriverInformationLength) PVOID DriverInformation,
+ __in ULONG DriverInformationLength,
+ __out_opt PULONG ReturnLength,
+ __in KPROCESSOR_MODE AccessMode
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PDRIVER_OBJECT driverObject;
+
+ if (
+ DriverInformationClass < DriverBasicInformation ||
+ DriverInformationClass >= MaxDriverInfoClass
+ )
+ return STATUS_INVALID_INFO_CLASS;
+
+ /* Probe user input. */
+ if (AccessMode != KernelMode)
+ {
+ __try
+ {
+ if (DriverInformation)
+ ProbeForWrite(DriverInformation, DriverInformationLength, 1);
+ if (ReturnLength)
+ ProbeForWrite(ReturnLength, sizeof(ULONG), 1);
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ return GetExceptionCode();
+ }
+ }
+
+ status = ObReferenceObjectByHandle(
+ DriverHandle,
+ 0,
+ *IoDriverObjectType,
+ KernelMode,
+ &driverObject,
+ NULL
+ );
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ __try
+ {
+ switch (DriverInformationClass)
+ {
+ /* DriverBasicInformation
+ *
+ * Basic information such as flags, driver base and driver size.
+ */
+ case DriverBasicInformation:
+ {
+ if (DriverInformation)
+ {
+ /* Check buffer length. */
+ if (DriverInformationLength == sizeof(DRIVER_BASIC_INFORMATION))
+ {
+ PDRIVER_BASIC_INFORMATION basicInfo;
+
+ basicInfo = (PDRIVER_BASIC_INFORMATION)DriverInformation;
+ basicInfo->Flags = driverObject->Flags;
+ basicInfo->DriverStart = driverObject->DriverStart;
+ basicInfo->DriverSize = driverObject->DriverSize;
+ }
+ else
+ {
+ status = STATUS_INFO_LENGTH_MISMATCH;
+ }
+ }
+
+ if (ReturnLength)
+ *ReturnLength = sizeof(DRIVER_BASIC_INFORMATION);
+ }
+ break;
+
+ /* DriverNameInformation
+ *
+ * The name of the driver - e.g. \Driver\KProcessHacker.
+ */
+ case DriverNameInformation:
+ {
+ if (DriverInformation)
+ {
+ /* Check buffer length. */
+ if (
+ sizeof(UNICODE_STRING) +
+ driverObject->DriverName.Length <=
+ DriverInformationLength
+ )
+ {
+ KphpCopyInfoUnicodeString(
+ DriverInformation,
+ &driverObject->DriverName
+ );
+ }
+ else
+ {
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+ }
+
+ /* Pass the ReturnLength. */
+ if (ReturnLength)
+ *ReturnLength = sizeof(UNICODE_STRING) + driverObject->DriverName.Length;
+ }
+ break;
+
+ /* DriverServiceKeyNameInformation
+ *
+ * The name of the driver's service key - e.g. \REGISTRY\...
+ */
+ case DriverServiceKeyNameInformation:
+ {
+ if (driverObject->DriverExtension)
+ {
+ if (DriverInformation)
+ {
+ if (
+ sizeof(UNICODE_STRING) +
+ driverObject->DriverExtension->ServiceKeyName.Length <=
+ DriverInformationLength
+ )
+ {
+ KphpCopyInfoUnicodeString(
+ DriverInformation,
+ &driverObject->DriverExtension->ServiceKeyName
+ );
+ }
+ else
+ {
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+ }
+
+ if (ReturnLength)
+ *ReturnLength = sizeof(UNICODE_STRING) +
+ driverObject->DriverExtension->ServiceKeyName.Length;
+ }
+ else
+ {
+ if (DriverInformation)
+ {
+ if (sizeof(UNICODE_STRING) <= DriverInformationLength)
+ {
+ /* Zero the information buffer. */
+ KphpCopyInfoUnicodeString(
+ DriverInformation,
+ NULL
+ );
+ }
+ else
+ {
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+ }
+
+ if (ReturnLength)
+ *ReturnLength = sizeof(UNICODE_STRING);
+ }
+ }
+ break;
+
+ default:
+ {
+ status = STATUS_INVALID_INFO_CLASS;
+ }
+ }
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ status = GetExceptionCode();
+ }
+
+ ObDereferenceObject(driverObject);
+
+ return status;
+}
+
+/* KphpCopyInfoUnicodeString
+ *
+ * Copies a UNICODE_STRING to an information buffer. If
+ * the given string is NULL, the function zeros the
+ * destination UNICODE_STRING.
+ */
+VOID KphpCopyInfoUnicodeString(
+ __out PVOID Information,
+ __in PUNICODE_STRING UnicodeString
+ )
+{
+ PUNICODE_STRING targetUnicodeString = (PUNICODE_STRING)Information;
+
+ if (UnicodeString)
+ {
+ targetUnicodeString->Length = UnicodeString->Length;
+ targetUnicodeString->MaximumLength = targetUnicodeString->Length;
+ targetUnicodeString->Buffer = (PWSTR)((PCHAR)Information + sizeof(UNICODE_STRING));
+ memcpy(
+ targetUnicodeString->Buffer,
+ UnicodeString->Buffer,
+ targetUnicodeString->Length
+ );
+ }
+ else
+ {
+ targetUnicodeString->Length = 0;
+ targetUnicodeString->MaximumLength = 0;
+ targetUnicodeString->Buffer = NULL;
+ }
+}
diff --git a/branches/ph-plugins/KProcessHacker/kph.c b/branches/ph-plugins/KProcessHacker/kph.c
new file mode 100644
index 000000000..0fb19d84d
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/kph.c
@@ -0,0 +1,373 @@
+/*
+ * Process Hacker Driver -
+ * custom APIs
+ *
+ * Copyright (C) 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 .
+ */
+
+#define _KPH_PRIVATE
+#include "include/kph.h"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, GetSystemRoutineAddress)
+#pragma alloc_text(PAGE, KphNtInit)
+#pragma alloc_text(PAGE, OpenProcess)
+#pragma alloc_text(PAGE, SetProcessToken)
+#endif
+
+/* GetSystemRoutineAddress
+ *
+ * Gets the address of a function exported by ntoskrnl or hal.
+ */
+PVOID GetSystemRoutineAddress(WCHAR *Name)
+{
+ UNICODE_STRING routineName;
+ PVOID routineAddress = NULL;
+
+ RtlInitUnicodeString(&routineName, Name);
+
+ /* Wrap in SEH because MmGetSystemRoutineAddress is known to cause
+ some BSODs. */
+ try
+ {
+ routineAddress = MmGetSystemRoutineAddress(&routineName);
+ }
+ except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ routineAddress = NULL;
+ }
+
+ return routineAddress;
+}
+
+/* KphNtInit
+ *
+ * Initializes the KProcessHacker NT component.
+ */
+NTSTATUS KphNtInit()
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ /* Confuse those damn AVs... */
+ PWCHAR keService = L"KeService"; // length 9, 18 bytes
+ PWCHAR descriptorTable = L"DescriptorTable"; // 15, 30 bytes
+ WCHAR keServiceDescriptorTable[9 + 15 + 1];
+
+ /* Reconstruct the string. */
+ memcpy(keServiceDescriptorTable, keService, 18);
+ memcpy(keServiceDescriptorTable + 9, descriptorTable, 30);
+ keServiceDescriptorTable[9 + 15] = L'\0';
+
+ /* Dynamically get function pointers. */
+ __KeServiceDescriptorTable = GetSystemRoutineAddress(keServiceDescriptorTable);
+ dfprintf("KeServiceDescriptorTable: %#x\n", __KeServiceDescriptorTable);
+ PsGetProcessJob = GetSystemRoutineAddress(L"PsGetProcessJob");
+ dfprintf("PsGetProcessJob: %#x\n", PsGetProcessJob);
+ PsResumeProcess = GetSystemRoutineAddress(L"PsResumeProcess");
+ dfprintf("PsResumeProcess: %#x\n", PsResumeProcess);
+ PsSuspendProcess = GetSystemRoutineAddress(L"PsSuspendProcess");
+ dfprintf("PsSuspendProcess: %#x\n", PsSuspendProcess);
+
+ if (WindowsVersion >= WINDOWS_7)
+ {
+ ObGetObjectType = GetSystemRoutineAddress(L"ObGetObjectType");
+ dfprintf("ObGetObjectType: %#x\n", ObGetObjectType);
+ }
+
+ /* Scan for functions. */
+ if (KiFastCallEntryScan.Initialized)
+ {
+ __KiFastCallEntry = KvScanProc(&KiFastCallEntryScan);
+ dfprintf("KiFastCallEntry+x: %#x\n", __KiFastCallEntry);
+ }
+ if (PsTerminateProcessScan.Initialized)
+ {
+ __PsTerminateProcess = KvScanProc(&PsTerminateProcessScan);
+ dfprintf("PsTerminateProcess: %#x\n", __PsTerminateProcess);
+ }
+ if (PspTerminateThreadByPointerScan.Initialized)
+ {
+ __PspTerminateThreadByPointer = KvScanProc(&PspTerminateThreadByPointerScan);
+ dfprintf("PspTerminateThreadByPointer: %#x\n", __PspTerminateThreadByPointer);
+ }
+
+ return status;
+}
+
+/* KphAttachProcess
+ *
+ * Attaches to a process represented by the specified EPROCESS.
+ */
+VOID KphAttachProcess(
+ __in PEPROCESS Process,
+ __out PKPH_ATTACH_STATE AttachState
+ )
+{
+ AttachState->Attached = FALSE;
+
+ /* Don't attach if we are already attached to the target. */
+ if (Process != PsGetCurrentProcess())
+ {
+ KeStackAttachProcess(Process, &AttachState->ApcState);
+ AttachState->Attached = TRUE;
+ AttachState->Process = Process;
+ }
+}
+
+/* KphAttachProcessHandle
+ *
+ * Attaches to a process represented by the specified handle.
+ */
+NTSTATUS KphAttachProcessHandle(
+ __in HANDLE ProcessHandle,
+ __out PKPH_ATTACH_STATE AttachState
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PEPROCESS processObject;
+
+ AttachState->Attached = FALSE;
+
+ status = ObReferenceObjectByHandle(
+ ProcessHandle,
+ 0,
+ *PsProcessType,
+ KernelMode,
+ &processObject,
+ NULL
+ );
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ KphAttachProcess(processObject, AttachState);
+ ObDereferenceObject(processObject);
+
+ return status;
+}
+
+/* KphAttachProcessId
+ *
+ * Attaches to a process represented by the specified process ID.
+ */
+NTSTATUS KphAttachProcessId(
+ __in HANDLE ProcessId,
+ __out PKPH_ATTACH_STATE AttachState
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PEPROCESS processObject;
+
+ AttachState->Attached = FALSE;
+
+ status = PsLookupProcessByProcessId(ProcessId, &processObject);
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ KphAttachProcess(processObject, AttachState);
+ ObDereferenceObject(processObject);
+
+ return status;
+}
+
+/* KphCaptureUnicodeString
+ *
+ * Captures a UNICODE_STRING. This function will not throw exceptions.
+ */
+NTSTATUS KphCaptureUnicodeString(
+ __in PUNICODE_STRING UnicodeString,
+ __out PUNICODE_STRING CapturedUnicodeString
+ )
+{
+ __try
+ {
+ CapturedUnicodeString->Length = UnicodeString->Length;
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ return GetExceptionCode();
+ }
+
+ CapturedUnicodeString->MaximumLength = CapturedUnicodeString->Length;
+ CapturedUnicodeString->Buffer = ExAllocatePoolWithTag(
+ PagedPool,
+ CapturedUnicodeString->Length,
+ TAG_CAPTURED_UNICODE_STRING
+ );
+
+ if (!CapturedUnicodeString->Buffer)
+ return STATUS_INSUFFICIENT_RESOURCES;
+
+ __try
+ {
+ memcpy(
+ CapturedUnicodeString->Buffer,
+ UnicodeString->Buffer,
+ CapturedUnicodeString->Length
+ );
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ KphFreeCapturedUnicodeString(CapturedUnicodeString);
+ return GetExceptionCode();
+ }
+
+ return STATUS_SUCCESS;
+}
+
+/* KphDetachProcess
+ *
+ * Detaches from the currently attached process.
+ */
+VOID KphDetachProcess(
+ __in PKPH_ATTACH_STATE AttachState
+ )
+{
+ if (AttachState->Attached)
+ KeUnstackDetachProcess(&AttachState->ApcState);
+}
+
+/* KphFreeCapturedUnicodeString
+ *
+ * Frees a UNICODE_STRING captured by KphCaptureUnicodeString.
+ */
+VOID KphFreeCapturedUnicodeString(
+ __in PUNICODE_STRING CapturedUnicodeString
+ )
+{
+ ExFreePoolWithTag(
+ CapturedUnicodeString->Buffer,
+ TAG_CAPTURED_UNICODE_STRING
+ );
+}
+
+/* KphProbeForReadUnicodeString
+ *
+ * Probes a UNICODE_STRING structure for reading.
+ */
+VOID KphProbeForReadUnicodeString(
+ __in PUNICODE_STRING UnicodeString
+ )
+{
+ ProbeForRead(UnicodeString, sizeof(UNICODE_STRING), 1);
+ ProbeForRead(UnicodeString->Buffer, UnicodeString->Length, 1);
+}
+
+/* KphProbeSystemAddressRange
+ *
+ * Probes an address range in kernel-mode memory for reading.
+ */
+VOID KphProbeSystemAddressRange(
+ __in PVOID BaseAddress,
+ __in ULONG Length
+ )
+{
+ ULONG_PTR page, pageEnd;
+
+ /* HACK HACK HACK HACK HACK HACK */
+ /* Check the address range by checking each page. */
+ /* Round down the base address to the page size. Note: please make sure you are
+ * not using a dumbass compiler which optimizes the following line by removing
+ * the divide and multiply.
+ */
+ page = (ULONG_PTR)BaseAddress / PAGE_SIZE * PAGE_SIZE;
+ /* BaseAddress + Length - 1 is the last address we will be reading. */
+ pageEnd = ((ULONG_PTR)BaseAddress + Length - 1) / PAGE_SIZE * PAGE_SIZE;
+
+ for (; page <= pageEnd; page += PAGE_SIZE)
+ {
+ /* Check the page. */
+ if (!MmIsAddressValid((PVOID)page))
+ ExRaiseStatus(STATUS_ACCESS_VIOLATION);
+ }
+}
+
+/* OpenProcess
+ *
+ * Opens the process with the specified PID.
+ */
+NTSTATUS OpenProcess(
+ __out PHANDLE ProcessHandle,
+ __in ACCESS_MASK DesiredAccess,
+ __in HANDLE ProcessId
+ )
+{
+ OBJECT_ATTRIBUTES objAttr = { 0 };
+ CLIENT_ID clientId;
+
+ objAttr.Length = sizeof(objAttr);
+ clientId.UniqueThread = 0;
+ clientId.UniqueProcess = ProcessId;
+
+ return KphOpenProcess(ProcessHandle, DesiredAccess, &objAttr, &clientId, KernelMode);
+}
+
+/* SetProcessToken
+ *
+ * Assigns the primary token of the target process from the
+ * primary token of source process.
+ */
+NTSTATUS SetProcessToken(
+ __in HANDLE sourcePid,
+ __in HANDLE targetPid
+ )
+{
+ NTSTATUS status;
+ HANDLE source;
+
+ if (NT_SUCCESS(status = OpenProcess(&source, PROCESS_QUERY_INFORMATION, sourcePid)))
+ {
+ HANDLE target;
+
+ if (NT_SUCCESS(status = OpenProcess(&target, PROCESS_QUERY_INFORMATION |
+ PROCESS_SET_INFORMATION, targetPid)))
+ {
+ HANDLE sourceToken;
+
+ if (NT_SUCCESS(status = KphOpenProcessTokenEx(source, TOKEN_DUPLICATE, 0,
+ &sourceToken, UserMode)))
+ {
+ HANDLE dupSourceToken;
+ OBJECT_ATTRIBUTES objectAttributes = { 0 };
+
+ objectAttributes.Length = sizeof(objectAttributes);
+
+ if (NT_SUCCESS(status = ZwDuplicateToken(sourceToken, TOKEN_ASSIGN_PRIMARY, &objectAttributes,
+ FALSE, TokenPrimary, &dupSourceToken)))
+ {
+ PROCESS_ACCESS_TOKEN token;
+
+ token.Token = dupSourceToken;
+ token.Thread = 0;
+
+ status = ZwSetInformationProcess(target, ProcessAccessToken, &token, sizeof(token));
+ }
+
+ ZwClose(dupSourceToken);
+ }
+
+ ZwClose(sourceToken);
+ }
+
+ ZwClose(target);
+ }
+
+ ZwClose(source);
+
+ return status;
+}
diff --git a/branches/ph-plugins/KProcessHacker/kprocesshacker.c b/branches/ph-plugins/KProcessHacker/kprocesshacker.c
new file mode 100644
index 000000000..6b2249298
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/kprocesshacker.c
@@ -0,0 +1,2367 @@
+/*
+ * Process Hacker Driver -
+ * main driver code
+ *
+ * Copyright (C) 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 .
+ */
+
+#include "include/kprocesshacker.h"
+#include "include/debug.h"
+
+#include "include/kph.h"
+#include "include/protect.h"
+#include "include/ps.h"
+#include "include/sysservice.h"
+#include "include/version.h"
+
+#define CHECK_IN_LENGTH \
+ if (inLength < sizeof(*args)) \
+ { \
+ status = STATUS_BUFFER_TOO_SMALL; \
+ goto IoControlEnd; \
+ }
+#define CHECK_OUT_LENGTH \
+ if (outLength < sizeof(*ret)) \
+ { \
+ status = STATUS_BUFFER_TOO_SMALL; \
+ goto IoControlEnd; \
+ }
+#define CHECK_IN_OUT_LENGTH \
+ if (inLength < sizeof(*args) || outLength < sizeof(*ret)) \
+ { \
+ status = STATUS_BUFFER_TOO_SMALL; \
+ goto IoControlEnd; \
+ }
+
+PDRIVER_OBJECT KphDriverObject;
+
+static PKPH_OBJECT_TYPE ClientEntryType;
+static LIST_ENTRY ClientListHead;
+static EX_PUSH_LOCK ClientListLock;
+
+static BOOLEAN ProtectionInitialized = FALSE;
+static FAST_MUTEX ProtectionMutex;
+
+static ULONG SsStartCount = 0;
+static FAST_MUTEX SsMutex;
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, DriverEntry)
+#pragma alloc_text(PAGE, DriverUnload)
+#pragma alloc_text(PAGE, KphDispatchCreate)
+#pragma alloc_text(PAGE, KphDispatchClose)
+#pragma alloc_text(PAGE, KphDispatchDeviceControl)
+#pragma alloc_text(PAGE, KphDispatchRead)
+#pragma alloc_text(PAGE, KphUnsupported)
+#endif
+
+NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ int i;
+ PDEVICE_OBJECT deviceObject = NULL;
+ UNICODE_STRING deviceName, dosDeviceName;
+
+ KphDriverObject = DriverObject;
+
+ /* Initialize version information. */
+ status = KvInit();
+
+ if (!NT_SUCCESS(status))
+ {
+ if (status == STATUS_NOT_SUPPORTED)
+ dprintf("Your operating system is not supported by KProcessHacker\n");
+
+ return status;
+ }
+
+ /* Initialize NT KPH. */
+ status = KphNtInit();
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ /* Initialize hooking. */
+ status = KphHookInit();
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ /* Initialize the KPH object manager. */
+ status = KphRefInit();
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ /* Initialize system service logging. */
+ status = KphSsLogInit();
+
+ if (!NT_SUCCESS(status))
+ {
+ KphRefDeinit();
+ return status;
+ }
+
+ /* Initialize trace databases. */
+ status = KphTraceDatabaseInitialization();
+
+ if (!NT_SUCCESS(status))
+ {
+ KphRefDeinit();
+ return status;
+ }
+
+ /* Initialize client list structures. */
+ InitializeListHead(&ClientListHead);
+ ExInitializePushLock(&ClientListLock);
+
+ status = KphCreateObjectType(
+ &ClientEntryType,
+ PagedPool,
+ 0,
+ ClientEntryDeleteProcedure
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ KphRefDeinit();
+ return status;
+ }
+
+ /* Initialize process protection. */
+ ExInitializeFastMutex(&ProtectionMutex);
+ /* Initialize the system service logging mutex. */
+ ExInitializeFastMutex(&SsMutex);
+
+ RtlInitUnicodeString(&deviceName, KPH_DEVICE_NAME);
+ RtlInitUnicodeString(&dosDeviceName, KPH_DEVICE_DOS_NAME);
+
+ /* Create the KProcessHacker device. */
+ status = IoCreateDevice(DriverObject, 0, &deviceName,
+ FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &deviceObject);
+
+ /* Set up the major functions. */
+ for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
+ DriverObject->MajorFunction[i] = NULL;
+
+ DriverObject->MajorFunction[IRP_MJ_CLOSE] = KphDispatchClose;
+ DriverObject->MajorFunction[IRP_MJ_CREATE] = KphDispatchCreate;
+ DriverObject->MajorFunction[IRP_MJ_READ] = KphDispatchRead;
+ DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = KphDispatchDeviceControl;
+ DriverObject->DriverUnload = DriverUnload;
+
+ deviceObject->Flags |= DO_BUFFERED_IO;
+ deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
+
+ IoCreateSymbolicLink(&dosDeviceName, &deviceName);
+
+ dprintf("Driver loaded\n");
+
+ return STATUS_SUCCESS;
+}
+
+VOID DriverUnload(PDRIVER_OBJECT DriverObject)
+{
+ UNICODE_STRING dosDeviceName;
+
+ RtlInitUnicodeString(&dosDeviceName, KPH_DEVICE_DOS_NAME);
+ IoDeleteSymbolicLink(&dosDeviceName);
+ IoDeleteDevice(DriverObject->DeviceObject);
+
+ ExAcquireFastMutex(&ProtectionMutex);
+
+ if (ProtectionInitialized)
+ {
+ KphProtectDeinit();
+ ProtectionInitialized = FALSE;
+ }
+
+ ExReleaseFastMutex(&ProtectionMutex);
+
+ /* Make sure system service logging is disabled. */
+ if (SsStartCount > 0)
+ SsUnref(SsStartCount);
+
+ /* Free system service logging structures. */
+ KphSsLogDeinit();
+
+ /* Free all objects in the object manager. */
+ KphRefDeinit();
+
+ dprintf("Driver unloaded\n");
+}
+
+NTSTATUS KphDispatchCreate(PDEVICE_OBJECT DeviceObject, PIRP Irp)
+{
+ NTSTATUS status = STATUS_SUCCESS;
+
+#ifdef KPH_REQUIRE_DEBUG_PRIVILEGE
+ if (!SeSinglePrivilegeCheck(SeExports->SeDebugPrivilege, UserMode))
+ {
+ dprintf("Client (PID %d) was refused\n", PsGetCurrentProcessId());
+ Irp->IoStatus.Status = STATUS_PRIVILEGE_NOT_HELD;
+
+ return STATUS_PRIVILEGE_NOT_HELD;
+ }
+#endif
+
+ /* Add a client entry. Note that we don't dereference it because
+ * we keep one reference for it being on the client list.
+ */
+ if (!CreateClientEntry(NULL))
+ {
+ Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ dprintf("Client (PID %d) connected\n", PsGetCurrentProcessId());
+ dprintf("Base IOCTL is 0x%08x\n", KPH_CTL_CODE(0));
+
+ return status;
+}
+
+NTSTATUS KphDispatchClose(PDEVICE_OBJECT DeviceObject, PIRP Irp)
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PKPH_CLIENT_ENTRY clientEntry;
+
+ ExAcquireFastMutex(&ProtectionMutex);
+
+ if (ProtectionInitialized)
+ {
+ ULONG count = KphProtectRemoveByTag(PsGetCurrentProcessId());
+ dprintf("Removed %d protection entries\n", count);
+ }
+
+ ExReleaseFastMutex(&ProtectionMutex);
+
+ /* Get the current client entry and dereference it twice to remove it. */
+ clientEntry = ReferenceClientEntry(NULL);
+
+ if (clientEntry)
+ KphDereferenceObjectEx(clientEntry, 2, FALSE);
+
+ dprintf("Client (PID %d) disconnected\n", PsGetCurrentProcessId());
+
+ return status;
+}
+
+VOID InitProtection()
+{
+ ExAcquireFastMutex(&ProtectionMutex);
+
+ if (!ProtectionInitialized)
+ {
+ if (NT_SUCCESS(KphProtectInit()))
+ ProtectionInitialized = TRUE;
+ }
+
+ ExReleaseFastMutex(&ProtectionMutex);
+}
+
+VOID SsRef(LONG count)
+{
+ LONG oldRefCount;
+
+ ASSERT(count >= 0);
+
+ if (count == 0)
+ return;
+
+ ExAcquireFastMutex(&SsMutex);
+
+ /* Add references. */
+ oldRefCount = InterlockedExchangeAdd(&SsStartCount, count);
+ ASSERT(oldRefCount >= 0);
+
+ /* Start system service logging if this was the first bunch of references. */
+ if (oldRefCount == 0)
+ KphSsLogStart();
+
+ ExReleaseFastMutex(&SsMutex);
+}
+
+VOID SsUnref(LONG count)
+{
+ LONG oldRefCount;
+
+ ASSERT(count >= 0);
+
+ if (count == 0)
+ return;
+
+ ExAcquireFastMutex(&SsMutex);
+
+ oldRefCount = InterlockedExchangeAdd(&SsStartCount, -count);
+ ASSERT(oldRefCount > 0);
+
+ if (oldRefCount - count == 0)
+ KphSsLogStop();
+
+ ExReleaseFastMutex(&SsMutex);
+}
+
+VOID NTAPI ClientEntryDeleteProcedure(
+ __in PVOID Object,
+ __in ULONG Flags
+ )
+{
+ PKPH_CLIENT_ENTRY entry = (PKPH_CLIENT_ENTRY)Object;
+
+ /* Lower the SS start count. */
+ SsUnref(entry->SsStartCount);
+
+ /* Free the handle table. */
+ KphFreeHandleTable(entry->HandleTable);
+
+ /* Remove the entry from the client list. */
+ KeEnterCriticalRegion();
+ ExAcquirePushLockExclusive(&ClientListLock);
+ RemoveEntryList(&entry->ClientListEntry);
+ ExReleasePushLock(&ClientListLock);
+ KeLeaveCriticalRegion();
+}
+
+PKPH_CLIENT_ENTRY CreateClientEntry(
+ __in_opt HANDLE ProcessId
+ )
+{
+ PKPH_CLIENT_ENTRY entry;
+ PKPH_HANDLE_TABLE handleTable;
+
+ /* If the PID wasn't specified, use the current one. */
+ if (!ProcessId)
+ ProcessId = PsGetCurrentProcessId();
+
+ if (!NT_SUCCESS(KphCreateHandleTable(
+ &handleTable,
+ KPH_CLIENT_MAXHANDLES,
+ sizeof(KPH_HANDLE_TABLE_ENTRY),
+ TAG_CLIENT_HANDLETABLE
+ )))
+ return NULL;
+
+ if (!NT_SUCCESS(KphCreateObject(
+ &entry,
+ sizeof(KPH_CLIENT_ENTRY),
+ 0,
+ ClientEntryType,
+ 0
+ )))
+ {
+ KphFreeHandleTable(handleTable);
+ return NULL;
+ }
+
+ /* Initialize the entry. */
+ entry->ProcessId = ProcessId;
+ entry->HandleTable = handleTable;
+ KphInitializeGuardedLock(&entry->SsLock, FALSE);
+ entry->SsStartCount = 0;
+
+ /* Insert the entry into the client list. */
+ KeEnterCriticalRegion();
+ ExAcquirePushLockExclusive(&ClientListLock);
+ InsertHeadList(&ClientListHead, &entry->ClientListEntry);
+ ExReleasePushLock(&ClientListLock);
+ KeLeaveCriticalRegion();
+
+ return entry;
+}
+
+PKPH_CLIENT_ENTRY ReferenceClientEntry(
+ __in_opt HANDLE ProcessId
+ )
+{
+ PLIST_ENTRY entry = ClientListHead.Flink;
+
+ /* If the PID wasn't specified, use the current one. */
+ if (!ProcessId)
+ ProcessId = PsGetCurrentProcessId();
+
+ KeEnterCriticalRegion();
+ ExAcquirePushLockShared(&ClientListLock);
+
+ /* Find the client entry. */
+ while (entry != &ClientListHead)
+ {
+ PKPH_CLIENT_ENTRY clientEntry =
+ CONTAINING_RECORD(entry, KPH_CLIENT_ENTRY, ClientListEntry);
+
+ if (clientEntry->ProcessId == ProcessId)
+ {
+ PKPH_CLIENT_ENTRY returnEntry = NULL;
+
+ /* Reference and return the entry. */
+ if (KphReferenceObjectSafe(clientEntry))
+ {
+ returnEntry = clientEntry;
+ }
+
+ ExReleasePushLock(&ClientListLock);
+ KeLeaveCriticalRegion();
+
+ return returnEntry;
+ }
+
+ entry = entry->Flink;
+ }
+
+ ExReleasePushLock(&ClientListLock);
+ KeLeaveCriticalRegion();
+
+ return NULL;
+}
+
+NTSTATUS CloseClientHandle(
+ __in_opt HANDLE ProcessId,
+ __in HANDLE Handle
+ )
+{
+ NTSTATUS status;
+ PKPH_CLIENT_ENTRY clientEntry;
+
+ clientEntry = ReferenceClientEntry(ProcessId);
+
+ if (!clientEntry)
+ return STATUS_UNSUCCESSFUL;
+
+ status = KphCloseHandle(clientEntry->HandleTable, Handle);
+ KphDereferenceObject(clientEntry);
+
+ return status;
+}
+
+NTSTATUS CreateClientHandle(
+ __in_opt HANDLE ProcessId,
+ __in PVOID Object,
+ __out PHANDLE Handle
+ )
+{
+ NTSTATUS status;
+ PKPH_CLIENT_ENTRY clientEntry;
+
+ clientEntry = ReferenceClientEntry(ProcessId);
+
+ if (!clientEntry)
+ return STATUS_UNSUCCESSFUL;
+
+ status = KphCreateHandle(clientEntry->HandleTable, Object, Handle);
+ KphDereferenceObject(clientEntry);
+
+ return status;
+}
+
+NTSTATUS ReferenceClientHandle(
+ __in_opt HANDLE ProcessId,
+ __in HANDLE Handle,
+ __in PKPH_OBJECT_TYPE ObjectType,
+ __out PVOID *Object
+ )
+{
+ NTSTATUS status;
+ PKPH_CLIENT_ENTRY clientEntry;
+
+ clientEntry = ReferenceClientEntry(ProcessId);
+
+ if (!clientEntry)
+ return STATUS_UNSUCCESSFUL;
+
+ status = KphReferenceObjectByHandle(
+ clientEntry->HandleTable,
+ Handle,
+ ObjectType,
+ Object
+ );
+ KphDereferenceObject(clientEntry);
+
+ return status;
+}
+
+PCHAR GetIoControlName(ULONG ControlCode)
+{
+ switch (ControlCode)
+ {
+ case KPH_CLOSEHANDLE:
+ return "Client Close Handle";
+ case KPH_SSQUERYCLIENTENTRY:
+ return "SsQueryClientEntry";
+ case KPH_OPENPROCESS:
+ return "KphOpenProcess";
+ case KPH_OPENTHREAD:
+ return "KphOpenThread";
+ case KPH_OPENPROCESSTOKEN:
+ return "KphOpenProcessTokenEx";
+ case KPH_GETPROCESSPROTECTED:
+ return "Get Process Protected";
+ case KPH_SETPROCESSPROTECTED:
+ return "Set Process Protected";
+ case KPH_TERMINATEPROCESS:
+ return "KphTerminateProcess";
+ case KPH_SUSPENDPROCESS:
+ return "KphSuspendProcess";
+ case KPH_RESUMEPROCESS:
+ return "KphResumeProcess";
+ case KPH_READVIRTUALMEMORY:
+ return "KphReadVirtualMemory";
+ case KPH_WRITEVIRTUALMEMORY:
+ return "KphWriteVirtualMemory";
+ case KPH_SETPROCESSTOKEN:
+ return "Set Process Token";
+ case KPH_GETTHREADSTARTADDRESS:
+ return "Get Thread Start Address";
+ case KPH_SETHANDLEATTRIBUTES:
+ return "Set Handle Attributes";
+ case KPH_GETHANDLEOBJECTNAME:
+ return "Get Handle Object Name";
+ case KPH_OPENPROCESSJOB:
+ return "KphOpenProcessJob";
+ case KPH_GETCONTEXTTHREAD:
+ return "KphGetContextThread";
+ case KPH_SETCONTEXTTHREAD:
+ return "KphSetContextThread";
+ case KPH_GETTHREADWIN32THREAD:
+ return "KphGetThreadWin32Thread";
+ case KPH_DUPLICATEOBJECT:
+ return "KphDuplicateObject";
+ case KPH_ZWQUERYOBJECT:
+ return "ZwQueryObject";
+ case KPH_GETPROCESSID:
+ return "KphGetProcessId";
+ case KPH_GETTHREADID:
+ return "KphGetThreadId";
+ case KPH_TERMINATETHREAD:
+ return "KphTerminateThread";
+ case KPH_GETFEATURES:
+ return "Get Features";
+ case KPH_SETHANDLEGRANTEDACCESS:
+ return "KphSetHandleGrantedAccess";
+ case KPH_ASSIGNIMPERSONATIONTOKEN:
+ return "KphAssignImpersonationToken";
+ case KPH_PROTECTADD:
+ return "Add Process Protection";
+ case KPH_PROTECTREMOVE:
+ return "Remove Process Protection";
+ case KPH_PROTECTQUERY:
+ return "Query Process Protection";
+ case KPH_UNSAFEREADVIRTUALMEMORY:
+ return "KphUnsafeReadVirtualMemory";
+ case KPH_SETEXECUTEOPTIONS:
+ return "Set Execute Options";
+ case KPH_QUERYPROCESSHANDLES:
+ return "KphQueryProcessHandles";
+ case KPH_OPENTHREADPROCESS:
+ return "KphOpenThreadProcess";
+ case KPH_CAPTURESTACKBACKTRACETHREAD:
+ return "KphCaptureStackBackTraceThread";
+ case KPH_DANGEROUSTERMINATETHREAD:
+ return "KphDangerousTerminateThread";
+ case KPH_OPENDEVICE:
+ return "KphOpenDevice";
+ case KPH_OPENDRIVER:
+ return "KphOpenDriver";
+ case KPH_QUERYINFORMATIONDRIVER:
+ return "KphQueryInformationDriver";
+ case KPH_OPENDIRECTORYOBJECT:
+ return "KphOpenDirectoryObject";
+ case KPH_SSREF:
+ return "SsRef";
+ case KPH_SSUNREF:
+ return "SsUnref";
+ case KPH_SSCREATECLIENTENTRY:
+ return "SsCreateClientEntry";
+ case KPH_SSCREATERULESETENTRY:
+ return "SsCreateRuleSetEntry";
+ case KPH_SSREMOVERULE:
+ return "SsRemoveRule";
+ case KPH_SSADDPROCESSIDRULE:
+ return "SsAddProcessIdRule";
+ case KPH_SSADDTHREADIDRULE:
+ return "SsAddThreadIdRule";
+ case KPH_SSADDPREVIOUSMODERULE:
+ return "SsAddPreviousModeRule";
+ case KPH_SSADDNUMBERRULE:
+ return "SsAddNumberRule";
+ case KPH_SSENABLECLIENTENTRY:
+ return "SsEnableClientEntry";
+ default:
+ return "Unknown";
+ }
+}
+
+NTSTATUS KphDispatchDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp)
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PIO_STACK_LOCATION ioStackIrp = NULL;
+ PVOID dataBuffer;
+ ULONG controlCode;
+ ULONG inLength = 0;
+ ULONG outLength = 0;
+ ULONG retLength = 0;
+
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+ Irp->IoStatus.Information = 0;
+
+ ioStackIrp = IoGetCurrentIrpStackLocation(Irp);
+
+ if (ioStackIrp == NULL)
+ {
+ status = STATUS_INTERNAL_ERROR;
+ goto IoControlEnd;
+ }
+
+ dataBuffer = Irp->AssociatedIrp.SystemBuffer;
+
+ if (dataBuffer == NULL && (inLength != 0 || outLength != 0))
+ {
+ status = STATUS_BUFFER_TOO_SMALL;
+ goto IoControlEnd;
+ }
+
+ inLength = ioStackIrp->Parameters.DeviceIoControl.InputBufferLength;
+ outLength = ioStackIrp->Parameters.DeviceIoControl.OutputBufferLength;
+ controlCode = ioStackIrp->Parameters.DeviceIoControl.IoControlCode;
+
+ dprintf("IoControl 0x%08x (%s)\n", controlCode, GetIoControlName(controlCode));
+
+ /* 1-byte packing for KPH input/output structures. */
+ #include
+
+ switch (controlCode)
+ {
+ /* Client Close Handle
+ *
+ * Closes a handle opened by the client.
+ */
+ case KPH_CLOSEHANDLE:
+ {
+ struct
+ {
+ HANDLE Handle;
+ } *args = dataBuffer;
+ PKPH_CLIENT_ENTRY clientEntry;
+
+ CHECK_IN_LENGTH;
+
+ status = CloseClientHandle(NULL, args->Handle);
+ }
+ break;
+
+ /* SsQueryClientEntry
+ *
+ * Queries information about a client entry.
+ */
+ case KPH_SSQUERYCLIENTENTRY:
+ {
+ struct
+ {
+ HANDLE ClientEntryHandle;
+ PKPHSS_CLIENT_INFORMATION ClientInformation;
+ ULONG ClientInformationLength;
+ PULONG ReturnLength;
+ } *args = dataBuffer;
+ PKPHSS_CLIENT_ENTRY clientEntry;
+
+ CHECK_IN_LENGTH;
+
+ /* Reference the client entry. */
+ status = ReferenceClientHandle(
+ NULL,
+ args->ClientEntryHandle,
+ KphSsClientEntryType,
+ &clientEntry
+ );
+
+ if (!NT_SUCCESS(status))
+ goto IoControlEnd;
+
+ /* Query the client entry. */
+ status = KphSsQueryClientEntry(
+ clientEntry,
+ args->ClientInformation,
+ args->ClientInformationLength,
+ args->ReturnLength,
+ UserMode
+ );
+ KphDereferenceObject(clientEntry);
+ }
+ break;
+
+ /* KphOpenProcess
+ *
+ * Opens the specified process. This call will never fail unless:
+ * 1. PsLookupProcessByProcessId, ObOpenObjectByPointer or some lower-level
+ * function is hooked, or
+ * 2. The process is protected.
+ */
+ case KPH_OPENPROCESS:
+ {
+ struct
+ {
+ HANDLE ProcessId;
+ ACCESS_MASK DesiredAccess;
+ } *args = dataBuffer;
+ struct
+ {
+ HANDLE ProcessHandle;
+ } *ret = dataBuffer;
+ OBJECT_ATTRIBUTES objectAttributes = { 0 };
+ CLIENT_ID clientId;
+
+ CHECK_IN_OUT_LENGTH;
+
+ clientId.UniqueThread = 0;
+ clientId.UniqueProcess = args->ProcessId;
+ status = KphOpenProcess(
+ &ret->ProcessHandle,
+ args->DesiredAccess,
+ &objectAttributes,
+ &clientId,
+ KernelMode
+ );
+
+ if (!NT_SUCCESS(status))
+ goto IoControlEnd;
+
+ retLength = sizeof(*ret);
+ }
+ break;
+
+ /* KphOpenThread
+ *
+ * Opens the specified thread. This call will never fail unless:
+ * 1. PsLookupProcessThreadByCid, ObOpenObjectByPointer or some lower-level
+ * function is hooked, or
+ * 2. The thread's process is protected.
+ */
+ case KPH_OPENTHREAD:
+ {
+ struct
+ {
+ HANDLE ThreadId;
+ ACCESS_MASK DesiredAccess;
+ } *args = dataBuffer;
+ struct
+ {
+ HANDLE ThreadHandle;
+ } *ret = dataBuffer;
+ OBJECT_ATTRIBUTES objectAttributes = { 0 };
+ CLIENT_ID clientId;
+
+ CHECK_IN_OUT_LENGTH;
+
+ clientId.UniqueThread = args->ThreadId;
+ clientId.UniqueProcess = 0;
+ status = KphOpenThread(
+ &ret->ThreadHandle,
+ args->DesiredAccess,
+ &objectAttributes,
+ &clientId,
+ KernelMode
+ );
+
+ if (!NT_SUCCESS(status))
+ goto IoControlEnd;
+
+ retLength = sizeof(*ret);
+ }
+ break;
+
+ /* KphOpenProcessToken
+ *
+ * Opens the specified process' token. This call will never fail unless
+ * a low-level function is hooked.
+ */
+ case KPH_OPENPROCESSTOKEN:
+ {
+ struct
+ {
+ HANDLE ProcessHandle;
+ ACCESS_MASK DesiredAccess;
+ } *args = dataBuffer;
+ struct
+ {
+ HANDLE TokenHandle;
+ } *ret = dataBuffer;
+
+ CHECK_IN_OUT_LENGTH;
+
+ status = KphOpenProcessTokenEx(
+ args->ProcessHandle,
+ args->DesiredAccess,
+ 0,
+ &ret->TokenHandle,
+ KernelMode
+ );
+
+ if (!NT_SUCCESS(status))
+ goto IoControlEnd;
+
+ retLength = sizeof(*ret);
+ }
+ break;
+
+ /* Get Process Protected
+ *
+ * Gets whether the process is protected.
+ */
+ case KPH_GETPROCESSPROTECTED:
+ {
+ struct
+ {
+ HANDLE ProcessId;
+ } *args = dataBuffer;
+ struct
+ {
+ BOOLEAN IsProtected;
+ } *ret = dataBuffer;
+ PEPROCESS processObject;
+
+ CHECK_IN_OUT_LENGTH;
+
+ status = PsLookupProcessByProcessId(args->ProcessId, &processObject);
+
+ if (!NT_SUCCESS(status))
+ goto IoControlEnd;
+
+ ret->IsProtected =
+ (CHAR)GET_BIT(
+ *(PULONG)KVOFF(processObject, OffEpProtectedProcessOff),
+ OffEpProtectedProcessBit
+ );
+ ObDereferenceObject(processObject);
+ retLength = sizeof(*ret);
+ }
+ break;
+
+ /* Set Process Protected
+ *
+ * Sets whether the process is protected.
+ */
+ case KPH_SETPROCESSPROTECTED:
+ {
+ struct
+ {
+ HANDLE ProcessId;
+ BOOLEAN IsProtected;
+ } *args = dataBuffer;
+ PEPROCESS processObject;
+
+ CHECK_IN_LENGTH;
+
+ status = PsLookupProcessByProcessId(args->ProcessId, &processObject);
+
+ if (!NT_SUCCESS(status))
+ goto IoControlEnd;
+
+ if (args->IsProtected)
+ {
+ SET_BIT(
+ *(PULONG)KVOFF(processObject, OffEpProtectedProcessOff),
+ OffEpProtectedProcessBit
+ );
+ }
+ else
+ {
+ CLEAR_BIT(
+ *(PULONG)KVOFF(processObject, OffEpProtectedProcessOff),
+ OffEpProtectedProcessBit
+ );
+ }
+
+ ObDereferenceObject(processObject);
+ }
+ break;
+
+ /* KphTerminateProcess
+ *
+ * Terminates the specified process. This call will never fail unless
+ * PsTerminateProcess could not be located and Zw/NtTerminateProcess
+ * is hooked, or an attempt was made to terminate the current process.
+ * In that case, the call will fail with STATUS_CANT_TERMINATE_SELF.
+ */
+ case KPH_TERMINATEPROCESS:
+ {
+ struct
+ {
+ HANDLE ProcessHandle;
+ NTSTATUS ExitStatus;
+ } *args = dataBuffer;
+
+ CHECK_IN_LENGTH;
+
+ status = KphTerminateProcess(args->ProcessHandle, args->ExitStatus);
+ }
+ break;
+
+ /* KphSuspendProcess
+ *
+ * Suspends the specified process. This call will fail on Windows XP
+ * and below.
+ */
+ case KPH_SUSPENDPROCESS:
+ {
+ struct
+ {
+ HANDLE ProcessHandle;
+ } *args = dataBuffer;
+
+ CHECK_IN_LENGTH;
+
+ status = KphSuspendProcess(args->ProcessHandle);
+ }
+ break;
+
+ /* KphResumeProcess
+ *
+ * Resumes the specified process. This call will fail on Windows XP
+ * and below.
+ */
+ case KPH_RESUMEPROCESS:
+ {
+ struct
+ {
+ HANDLE ProcessHandle;
+ } *args = dataBuffer;
+
+ CHECK_IN_LENGTH;
+
+ status = KphResumeProcess(args->ProcessHandle);
+ }
+ break;
+
+ /* KphReadVirtualMemory
+ *
+ * Reads process memory.
+ */
+ case KPH_READVIRTUALMEMORY:
+ {
+ struct
+ {
+ HANDLE ProcessHandle;
+ PVOID BaseAddress;
+ PVOID Buffer;
+ ULONG BufferLength;
+ PULONG ReturnLength;
+ } *args = dataBuffer;
+
+ CHECK_IN_LENGTH;
+
+ status = KphReadVirtualMemory(
+ args->ProcessHandle,
+ args->BaseAddress,
+ args->Buffer,
+ args->BufferLength,
+ args->ReturnLength,
+ UserMode
+ );
+ }
+ break;
+
+ /* KphWriteVirtualMemory
+ *
+ * Writes to process memory.
+ */
+ case KPH_WRITEVIRTUALMEMORY:
+ {
+ struct
+ {
+ HANDLE ProcessHandle;
+ PVOID BaseAddress;
+ PVOID Buffer;
+ ULONG BufferLength;
+ PULONG ReturnLength;
+ } *args = dataBuffer;
+
+ CHECK_IN_LENGTH;
+
+ status = KphWriteVirtualMemory(
+ args->ProcessHandle,
+ args->BaseAddress,
+ args->Buffer,
+ args->BufferLength,
+ args->ReturnLength,
+ UserMode
+ );
+ }
+ break;
+
+ /* Set Process Token
+ *
+ * Assigns the primary token of a source process to a target process.
+ */
+ case KPH_SETPROCESSTOKEN:
+ {
+ struct
+ {
+ HANDLE SourceProcessId;
+ HANDLE TargetProcessId;
+ } *args = dataBuffer;
+
+ CHECK_IN_LENGTH;
+
+ status = SetProcessToken(args->SourceProcessId, args->TargetProcessId);
+ }
+ break;
+
+ /* Get Thread Start Address
+ *
+ * Gets the specified thread's start address.
+ */
+ case KPH_GETTHREADSTARTADDRESS:
+ {
+ struct
+ {
+ HANDLE ThreadHandle;
+ } *args = dataBuffer;
+ struct
+ {
+ PVOID StartAddress;
+ } *ret = dataBuffer;
+ PETHREAD threadObject;
+
+ CHECK_IN_OUT_LENGTH;
+
+ status = ObReferenceObjectByHandle(args->ThreadHandle, 0, *PsThreadType, KernelMode, &threadObject, NULL);
+
+ if (!NT_SUCCESS(status))
+ goto IoControlEnd;
+
+ /* Get the Win32StartAddress */
+ if (!(ret->StartAddress = *(PVOID *)KVOFF(threadObject, OffEtWin32StartAddress)))
+ {
+ /* If that failed, get the StartAddress */
+ ret->StartAddress = *(PVOID *)KVOFF(threadObject, OffEtStartAddress);
+ }
+
+ ObDereferenceObject(threadObject);
+ retLength = sizeof(*ret);
+ }
+ break;
+
+ /* Set Handle Attributes
+ *
+ * Sets handle flags in the specified process.
+ */
+ case KPH_SETHANDLEATTRIBUTES:
+ {
+ struct
+ {
+ HANDLE ProcessHandle;
+ HANDLE Handle;
+ ULONG Flags;
+ } *args = dataBuffer;
+ KPH_ATTACH_STATE attachState;
+ OBJECT_HANDLE_FLAG_INFORMATION handleFlags = { 0 };
+
+ CHECK_IN_LENGTH;
+
+ status = KphAttachProcessHandle(args->ProcessHandle, &attachState);
+
+ if (!NT_SUCCESS(status))
+ goto IoControlEnd;
+
+ if (args->Flags & OBJ_PROTECT_CLOSE)
+ handleFlags.ProtectFromClose = TRUE;
+ if (args->Flags & OBJ_INHERIT)
+ handleFlags.Inherit = TRUE;
+
+ status = ObSetHandleAttributes(args->Handle, &handleFlags, UserMode);
+ KphDetachProcess(&attachState);
+ }
+ break;
+
+ /* Get Handle Object Name
+ *
+ * Gets the name of the specified handle. The handle can be remote; in
+ * that case a valid process handle must be passed. Otherwise, set the
+ * process handle to -1 (NtCurrentProcess()).
+ */
+ case KPH_GETHANDLEOBJECTNAME:
+ {
+ struct
+ {
+ HANDLE ProcessHandle;
+ HANDLE Handle;
+ } *args = dataBuffer;
+ KPH_ATTACH_STATE attachState;
+ PVOID object;
+
+ CHECK_IN_LENGTH;
+
+ status = KphAttachProcessHandle(args->ProcessHandle, &attachState);
+
+ if (!NT_SUCCESS(status))
+ goto IoControlEnd;
+
+ /* See the block for KPH_ZWQUERYOBJECT for information. */
+ if (attachState.Process == PsInitialSystemProcess)
+ MakeKernelHandle(args->Handle);
+
+ status = ObReferenceObjectByHandle(args->Handle, 0, NULL, KernelMode, &object, NULL);
+ KphDetachProcess(&attachState);
+
+ if (!NT_SUCCESS(status))
+ goto IoControlEnd;
+
+ status = KphQueryNameObject(object, (PUNICODE_STRING)dataBuffer, outLength, &retLength);
+ ObDereferenceObject(object);
+ }
+ break;
+
+ /* KphOpenProcessJob
+ *
+ * Opens the job object that the process is assigned to. If the process is
+ * not assigned to any job object, the call will fail with STATUS_PROCESS_NOT_IN_JOB.
+ */
+ case KPH_OPENPROCESSJOB:
+ {
+ struct
+ {
+ HANDLE ProcessHandle;
+ ACCESS_MASK DesiredAccess;
+ } *args = dataBuffer;
+ struct
+ {
+ HANDLE JobHandle;
+ } *ret = dataBuffer;
+
+ CHECK_IN_OUT_LENGTH;
+
+ status = KphOpenProcessJob(args->ProcessHandle, args->DesiredAccess, &ret->JobHandle, KernelMode);
+
+ if (!NT_SUCCESS(status))
+ goto IoControlEnd;
+
+ retLength = sizeof(*ret);
+ }
+ break;
+
+ /* KphGetContextThread
+ *
+ * Gets the context of the specified thread.
+ */
+ case KPH_GETCONTEXTTHREAD:
+ {
+ struct
+ {
+ HANDLE ThreadHandle;
+ PCONTEXT ThreadContext;
+ } *args = dataBuffer;
+
+ CHECK_IN_LENGTH;
+
+ status = KphGetContextThread(args->ThreadHandle, args->ThreadContext, UserMode);
+ }
+ break;
+
+ /* KphSetContextThread
+ *
+ * Sets the context of the specified thread.
+ */
+ case KPH_SETCONTEXTTHREAD:
+ {
+ struct
+ {
+ HANDLE ThreadHandle;
+ PCONTEXT ThreadContext;
+ } *args = dataBuffer;
+
+ CHECK_IN_LENGTH;
+
+ status = KphSetContextThread(args->ThreadHandle, args->ThreadContext, UserMode);
+ }
+ break;
+
+ /* KphGetThreadWin32Thread
+ *
+ * Gets a pointer to the specified thread's Win32Thread structure.
+ */
+ case KPH_GETTHREADWIN32THREAD:
+ {
+ struct
+ {
+ HANDLE ThreadHandle;
+ } *args = dataBuffer;
+ struct
+ {
+ PVOID Win32Thread;
+ } *ret = dataBuffer;
+
+ CHECK_IN_OUT_LENGTH;
+
+ status = KphGetThreadWin32Thread(args->ThreadHandle, &ret->Win32Thread, KernelMode);
+
+ if (!NT_SUCCESS(status))
+ goto IoControlEnd;
+
+ retLength = sizeof(*ret);
+ }
+ break;
+
+ /* KphDuplicateObject
+ *
+ * Duplicates the specified handle from the source process to the target process.
+ * Do not use this call to duplicate file handles; it may freeze indefinitely if
+ * the file is a named pipe.
+ */
+ case KPH_DUPLICATEOBJECT:
+ {
+ struct
+ {
+ HANDLE SourceProcessHandle;
+ HANDLE SourceHandle;
+ HANDLE TargetProcessHandle;
+ PHANDLE TargetHandle;
+ ACCESS_MASK DesiredAccess;
+ ULONG HandleAttributes;
+ ULONG Options;
+ } *args = dataBuffer;
+
+ CHECK_IN_LENGTH;
+
+ status = KphDuplicateObject(
+ args->SourceProcessHandle,
+ args->SourceHandle,
+ args->TargetProcessHandle,
+ args->TargetHandle,
+ args->DesiredAccess,
+ args->HandleAttributes,
+ args->Options,
+ UserMode
+ );
+ }
+ break;
+
+ /* ZwQueryObject
+ *
+ * Performs ZwQueryObject in the context of another process.
+ */
+ case KPH_ZWQUERYOBJECT:
+ {
+ struct
+ {
+ HANDLE ProcessHandle;
+ HANDLE Handle;
+ OBJECT_INFORMATION_CLASS ObjectInformationClass;
+ } *args = dataBuffer;
+ struct
+ {
+ NTSTATUS Status;
+ ULONG ReturnLength;
+ PVOID BufferBase;
+ CHAR Buffer[1];
+ } *ret = dataBuffer;
+ NTSTATUS status2 = STATUS_SUCCESS;
+ KPH_ATTACH_STATE attachState;
+ BOOLEAN attached;
+
+ if (inLength < sizeof(*args) || outLength < sizeof(*ret) - sizeof(CHAR))
+ {
+ status = STATUS_BUFFER_TOO_SMALL;
+ goto IoControlEnd;
+ }
+
+ status = KphAttachProcessHandle(args->ProcessHandle, &attachState);
+
+ if (!NT_SUCCESS(status))
+ goto IoControlEnd;
+
+ /* Are we attached to the system process? If we are,
+ * we must set the high bit in the handle to indicate
+ * that it is a kernel handle - a new check for this
+ * was added in Windows 7.
+ */
+ if (attachState.Process == PsInitialSystemProcess)
+ MakeKernelHandle(args->Handle);
+
+ status2 = ZwQueryObject(
+ args->Handle,
+ args->ObjectInformationClass,
+ ret->Buffer,
+ outLength - (sizeof(*ret) - sizeof(CHAR)),
+ &retLength
+ );
+ KphDetachProcess(&attachState);
+
+ ret->ReturnLength = retLength;
+ ret->BufferBase = ret->Buffer;
+
+ if (NT_SUCCESS(status2))
+ retLength += sizeof(*ret) - sizeof(CHAR);
+ else
+ retLength = sizeof(*ret) - sizeof(CHAR);
+
+ ret->Status = status2;
+ }
+ break;
+
+ /* KphGetProcessId
+ *
+ * Gets the process ID of a process handle in the context of another process.
+ */
+ case KPH_GETPROCESSID:
+ {
+ struct
+ {
+ HANDLE ProcessHandle;
+ HANDLE Handle;
+ } *args = dataBuffer;
+ struct
+ {
+ HANDLE ProcessId;
+ } *ret = dataBuffer;
+ KPH_ATTACH_STATE attachState;
+
+ CHECK_IN_OUT_LENGTH;
+
+ status = KphAttachProcessHandle(args->ProcessHandle, &attachState);
+
+ if (!NT_SUCCESS(status))
+ goto IoControlEnd;
+
+ if (attachState.Process == PsInitialSystemProcess)
+ MakeKernelHandle(args->Handle);
+
+ ret->ProcessId = KphGetProcessId(args->Handle);
+ KphDetachProcess(&attachState);
+ retLength = sizeof(*ret);
+ }
+ break;
+
+ /* KphGetThreadId
+ *
+ * Gets the thread ID of a thread handle in the context of another process.
+ */
+ case KPH_GETTHREADID:
+ {
+ struct
+ {
+ HANDLE ProcessHandle;
+ HANDLE Handle;
+ } *args = dataBuffer;
+ struct
+ {
+ HANDLE ThreadId;
+ HANDLE ProcessId;
+ } *ret = dataBuffer;
+ KPH_ATTACH_STATE attachState;
+
+ CHECK_IN_OUT_LENGTH;
+
+ status = KphAttachProcessHandle(args->ProcessHandle, &attachState);
+
+ if (!NT_SUCCESS(status))
+ goto IoControlEnd;
+
+ if (attachState.Process == PsInitialSystemProcess)
+ MakeKernelHandle(args->Handle);
+
+ ret->ThreadId = KphGetThreadId(args->Handle, &ret->ProcessId);
+ KphDetachProcess(&attachState);
+ retLength = sizeof(*ret);
+ }
+ break;
+
+ /* KphTerminateThread
+ *
+ * Terminates the specified thread. This call will fail if
+ * PspTerminateThreadByPointer could not be located or if an attempt
+ * was made to terminate the current thread. In that case, the call
+ * will return STATUS_CANT_TERMINATE_SELF.
+ */
+ case KPH_TERMINATETHREAD:
+ {
+ struct
+ {
+ HANDLE ThreadHandle;
+ NTSTATUS ExitStatus;
+ } *args = dataBuffer;
+
+ CHECK_IN_LENGTH;
+
+ status = KphTerminateThread(args->ThreadHandle, args->ExitStatus);
+ }
+ break;
+
+ /* Get Features
+ *
+ * Gets the features supported by the driver.
+ */
+ case KPH_GETFEATURES:
+ {
+ struct
+ {
+ ULONG Features;
+ } *ret = dataBuffer;
+ ULONG features = 0;
+
+ CHECK_OUT_LENGTH;
+
+ if (__PsTerminateProcess)
+ features |= KPHF_PSTERMINATEPROCESS;
+ if (__PspTerminateThreadByPointer)
+ features |= KPHF_PSPTERMINATETHREADBPYPOINTER;
+
+ ret->Features = features;
+ retLength = sizeof(*ret);
+ }
+ break;
+
+ /* KphSetHandleGrantedAccess
+ *
+ * Sets the granted access for a handle.
+ */
+ case KPH_SETHANDLEGRANTEDACCESS:
+ {
+ struct
+ {
+ HANDLE Handle;
+ ACCESS_MASK GrantedAccess;
+ } *args = dataBuffer;
+
+ CHECK_IN_LENGTH;
+
+ status = KphSetHandleGrantedAccess(
+ PsGetCurrentProcess(),
+ args->Handle,
+ args->GrantedAccess
+ );
+ }
+ break;
+
+ /* KphAssignImpersonationToken
+ *
+ * Assigns an impersonation token to a thread.
+ */
+ case KPH_ASSIGNIMPERSONATIONTOKEN:
+ {
+ struct
+ {
+ HANDLE ThreadHandle;
+ HANDLE TokenHandle;
+ } *args = dataBuffer;
+
+ CHECK_IN_LENGTH;
+
+ status = KphAssignImpersonationToken(args->ThreadHandle, args->TokenHandle);
+ }
+ break;
+
+ /* Add Process Protection */
+ case KPH_PROTECTADD:
+ {
+ struct
+ {
+ HANDLE ProcessHandle;
+ LOGICAL AllowKernelMode;
+ ACCESS_MASK ProcessAllowMask;
+ ACCESS_MASK ThreadAllowMask;
+ } *args = dataBuffer;
+ PEPROCESS processObject;
+
+ CHECK_IN_LENGTH;
+
+ /* We'll reference the process and then dereference it. That way
+ * we can get the address of the object - that's all we need.
+ */
+
+ status = ObReferenceObjectByHandle(
+ args->ProcessHandle,
+ 0,
+ *PsProcessType,
+ KernelMode,
+ &processObject,
+ NULL
+ );
+
+ if (!NT_SUCCESS(status))
+ goto IoControlEnd;
+
+ ObDereferenceObject(processObject);
+
+ InitProtection();
+
+ /* Don't protect the same process twice. */
+ if (KphProtectFindEntry(processObject, NULL, NULL))
+ {
+ status = STATUS_NOT_SUPPORTED;
+ goto IoControlEnd;
+ }
+
+ if (!KphProtectAddEntry(
+ processObject,
+ PsGetCurrentProcessId(),
+ args->AllowKernelMode,
+ args->ProcessAllowMask,
+ args->ThreadAllowMask
+ ))
+ {
+ status = STATUS_UNSUCCESSFUL;
+ goto IoControlEnd;
+ }
+ }
+ break;
+
+ /* Remove Process Protection */
+ case KPH_PROTECTREMOVE:
+ {
+ struct
+ {
+ HANDLE ProcessHandle;
+ } *args = dataBuffer;
+ PEPROCESS processObject;
+
+ /* Can't remove anything if process protection hasn't been initialized -
+ there isn't anything to remove. */
+ if (!ProtectionInitialized)
+ {
+ status = STATUS_INVALID_PARAMETER;
+ goto IoControlEnd;
+ }
+
+ CHECK_IN_LENGTH;
+
+ status = ObReferenceObjectByHandle(
+ args->ProcessHandle,
+ 0,
+ *PsProcessType,
+ KernelMode,
+ &processObject,
+ NULL
+ );
+
+ if (!NT_SUCCESS(status))
+ goto IoControlEnd;
+
+ ObDereferenceObject(processObject);
+
+ if (!KphProtectRemoveByProcess(processObject))
+ {
+ status = STATUS_UNSUCCESSFUL;
+ goto IoControlEnd;
+ }
+ }
+ break;
+
+ /* Query Process Protection */
+ case KPH_PROTECTQUERY:
+ {
+ struct
+ {
+ HANDLE ProcessHandle;
+ PLOGICAL AllowKernelMode;
+ PACCESS_MASK ProcessAllowMask;
+ PACCESS_MASK ThreadAllowMask;
+ } *args = dataBuffer;
+ PEPROCESS processObject;
+ KPH_PROCESS_ENTRY processEntry;
+
+ /* Can't query anything if process protection hasn't been initialized -
+ there isn't anything to query. */
+ if (!ProtectionInitialized)
+ {
+ status = STATUS_INVALID_PARAMETER;
+ goto IoControlEnd;
+ }
+
+ CHECK_IN_LENGTH;
+
+ __try
+ {
+ ProbeForWrite(args->AllowKernelMode, sizeof(LOGICAL), 1);
+ ProbeForWrite(args->ProcessAllowMask, sizeof(ACCESS_MASK), 1);
+ ProbeForWrite(args->ThreadAllowMask, sizeof(ACCESS_MASK), 1);
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ status = GetExceptionCode();
+ goto IoControlEnd;
+ }
+
+ status = ObReferenceObjectByHandle(
+ args->ProcessHandle,
+ 0,
+ *PsProcessType,
+ KernelMode,
+ &processObject,
+ NULL
+ );
+
+ if (!NT_SUCCESS(status))
+ goto IoControlEnd;
+
+ ObDereferenceObject(processObject);
+
+ if (!KphProtectFindEntry(processObject, NULL, &processEntry))
+ {
+ status = STATUS_UNSUCCESSFUL;
+ goto IoControlEnd;
+ }
+
+ __try
+ {
+ *(args->AllowKernelMode) = processEntry.AllowKernelMode;
+ *(args->ProcessAllowMask) = processEntry.ProcessAllowMask;
+ *(args->ThreadAllowMask) = processEntry.ThreadAllowMask;
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ status = GetExceptionCode();
+ }
+ }
+ break;
+
+ /* KphUnsafeReadVirtualMemory
+ *
+ * Reads process memory or kernel memory.
+ */
+ case KPH_UNSAFEREADVIRTUALMEMORY:
+ {
+ struct
+ {
+ HANDLE ProcessHandle;
+ PVOID BaseAddress;
+ PVOID Buffer;
+ ULONG BufferLength;
+ PULONG ReturnLength;
+ } *args = dataBuffer;
+
+ CHECK_IN_LENGTH;
+
+ status = KphUnsafeReadVirtualMemory(
+ args->ProcessHandle,
+ args->BaseAddress,
+ args->Buffer,
+ args->BufferLength,
+ args->ReturnLength,
+ UserMode
+ );
+ }
+ break;
+
+ /* Set Execute Options
+ *
+ * Sets NX status for a process.
+ */
+ case KPH_SETEXECUTEOPTIONS:
+ {
+ struct
+ {
+ HANDLE ProcessHandle;
+ ULONG ExecuteOptions;
+ } *args = dataBuffer;
+ KPH_ATTACH_STATE attachState;
+
+ CHECK_IN_LENGTH;
+
+ status = KphAttachProcessHandle(args->ProcessHandle, &attachState);
+
+ if (!NT_SUCCESS(status))
+ goto IoControlEnd;
+
+ status = ZwSetInformationProcess(
+ NtCurrentProcess(),
+ ProcessExecuteFlags,
+ &args->ExecuteOptions,
+ sizeof(ULONG)
+ );
+ KphDetachProcess(&attachState);
+ }
+ break;
+
+ /* KphQueryProcessHandles
+ *
+ * Gets the handles in a process handle table.
+ */
+ case KPH_QUERYPROCESSHANDLES:
+ {
+ struct
+ {
+ HANDLE ProcessHandle;
+ PVOID Buffer;
+ ULONG BufferLength;
+ PULONG ReturnLength;
+ } *args = dataBuffer;
+
+ CHECK_IN_LENGTH;
+
+ status = KphQueryProcessHandles(
+ args->ProcessHandle,
+ (PPROCESS_HANDLE_INFORMATION)args->Buffer,
+ args->BufferLength,
+ args->ReturnLength,
+ UserMode
+ );
+ }
+ break;
+
+ /* KphOpenThreadProcess
+ *
+ * Opens the process associated with the specified thread.
+ */
+ case KPH_OPENTHREADPROCESS:
+ {
+ struct
+ {
+ HANDLE ThreadHandle;
+ ACCESS_MASK DesiredAccess;
+ } *args = dataBuffer;
+ struct
+ {
+ HANDLE ProcessHandle;
+ } *ret = dataBuffer;
+
+ CHECK_IN_OUT_LENGTH;
+
+ status = KphOpenThreadProcess(
+ args->ThreadHandle,
+ args->DesiredAccess,
+ &ret->ProcessHandle,
+ KernelMode
+ );
+
+ if (!NT_SUCCESS(status))
+ goto IoControlEnd;
+
+ retLength = sizeof(*ret);
+ }
+ break;
+
+ /* KphCaptureStackBackTraceThread
+ *
+ * Captures a kernel stack trace for the specified thread.
+ */
+ case KPH_CAPTURESTACKBACKTRACETHREAD:
+ {
+ struct
+ {
+ HANDLE ThreadHandle;
+ ULONG FramesToSkip;
+ ULONG FramesToCapture;
+ PVOID *BackTrace;
+ PULONG CapturedFrames;
+ PULONG BackTraceHash;
+ } *args = dataBuffer;
+
+ CHECK_IN_LENGTH;
+
+ status = KphCaptureStackBackTraceThread(
+ args->ThreadHandle,
+ args->FramesToSkip,
+ args->FramesToCapture,
+ args->BackTrace,
+ args->CapturedFrames,
+ args->BackTraceHash,
+ UserMode
+ );
+ }
+ break;
+
+ /* KphDangerousTerminateThread
+ *
+ * Terminates the specified thread. This operation may cause a bugcheck.
+ */
+ case KPH_DANGEROUSTERMINATETHREAD:
+ {
+ struct
+ {
+ HANDLE ThreadHandle;
+ NTSTATUS ExitStatus;
+ } *args = dataBuffer;
+
+ CHECK_IN_LENGTH;
+
+ status = KphDangerousTerminateThread(args->ThreadHandle, args->ExitStatus);
+ }
+ break;
+
+ /* KphOpenDevice
+ *
+ * Opens a device object.
+ */
+ case KPH_OPENDEVICE:
+ {
+ struct
+ {
+ PHANDLE DeviceHandle;
+ POBJECT_ATTRIBUTES ObjectAttributes;
+ } *args = dataBuffer;
+
+ CHECK_IN_LENGTH;
+
+ status = KphOpenDevice(args->DeviceHandle, args->ObjectAttributes, UserMode);
+ }
+ break;
+
+ /* KphOpenDriver
+ *
+ * Opens a driver object.
+ */
+ case KPH_OPENDRIVER:
+ {
+ struct
+ {
+ PHANDLE DriverHandle;
+ POBJECT_ATTRIBUTES ObjectAttributes;
+ } *args = dataBuffer;
+
+ CHECK_IN_LENGTH;
+
+ status = KphOpenDriver(args->DriverHandle, args->ObjectAttributes, UserMode);
+ }
+ break;
+
+ /* KphQueryInformationDriver
+ *
+ * Queries information about a driver object.
+ */
+ case KPH_QUERYINFORMATIONDRIVER:
+ {
+ struct
+ {
+ HANDLE DriverHandle;
+ DRIVER_INFORMATION_CLASS DriverInformationClass;
+ PVOID DriverInformation;
+ ULONG DriverInformationLength;
+ PULONG ReturnLength;
+ } *args = dataBuffer;
+
+ CHECK_IN_LENGTH;
+
+ status = KphQueryInformationDriver(
+ args->DriverHandle,
+ args->DriverInformationClass,
+ args->DriverInformation,
+ args->DriverInformationLength,
+ args->ReturnLength,
+ UserMode
+ );
+ }
+ break;
+
+ /* KphOpenDirectoryObject
+ *
+ * Opens a directory object.
+ */
+ case KPH_OPENDIRECTORYOBJECT:
+ {
+ struct
+ {
+ PHANDLE DirectoryObjectHandle;
+ ACCESS_MASK DesiredAccess;
+ POBJECT_ATTRIBUTES ObjectAttributes;
+ } *args = dataBuffer;
+
+ CHECK_IN_LENGTH;
+
+ status = KphOpenDirectoryObject(
+ args->DirectoryObjectHandle,
+ args->DesiredAccess,
+ args->ObjectAttributes,
+ UserMode
+ );
+ }
+ break;
+
+ /* SsRef
+ *
+ * Adds a system service logging reference.
+ */
+ case KPH_SSREF:
+ {
+ PKPH_CLIENT_ENTRY clientEntry = ReferenceClientEntry(NULL);
+
+ if (!clientEntry)
+ {
+ status = STATUS_INTERNAL_ERROR;
+ goto IoControlEnd;
+ }
+
+ KphAcquireGuardedLock(&clientEntry->SsLock);
+
+ if (clientEntry->SsStartCount < KPH_CLIENT_SSMAXCOUNT)
+ {
+ clientEntry->SsStartCount++;
+ SsRef(1);
+ }
+ else
+ {
+ status = STATUS_UNSUCCESSFUL;
+ }
+
+ KphReleaseGuardedLock(&clientEntry->SsLock);
+
+ KphDereferenceObject(clientEntry);
+ }
+ break;
+
+ /* SsUnref
+ *
+ * Removes a system service logging reference.
+ */
+ case KPH_SSUNREF:
+ {
+ PKPH_CLIENT_ENTRY clientEntry = ReferenceClientEntry(NULL);
+
+ if (!clientEntry)
+ {
+ status = STATUS_INTERNAL_ERROR;
+ goto IoControlEnd;
+ }
+
+ KphAcquireGuardedLock(&clientEntry->SsLock);
+
+ if (clientEntry->SsStartCount > 0)
+ {
+ clientEntry->SsStartCount--;
+ SsUnref(1);
+ }
+ else
+ {
+ status = STATUS_UNSUCCESSFUL;
+ }
+
+ KphReleaseGuardedLock(&clientEntry->SsLock);
+
+ KphDereferenceObject(clientEntry);
+ }
+ break;
+
+ /* SsCreateClientEntry
+ *
+ * Creates a system service logging client entry.
+ */
+ case KPH_SSCREATECLIENTENTRY:
+ {
+ struct
+ {
+ HANDLE ProcessHandle;
+ HANDLE EventHandle;
+ HANDLE SemaphoreHandle;
+ PVOID BufferBase;
+ ULONG BufferSize;
+ } *args = dataBuffer;
+ struct
+ {
+ HANDLE ClientEntryHandle;
+ } *ret = dataBuffer;
+ PKPHSS_CLIENT_ENTRY clientEntry;
+
+ CHECK_IN_OUT_LENGTH;
+
+ status = KphSsCreateClientEntry(
+ &clientEntry,
+ args->ProcessHandle,
+ args->EventHandle,
+ args->SemaphoreHandle,
+ args->BufferBase,
+ args->BufferSize,
+ UserMode
+ );
+
+ if (!NT_SUCCESS(status))
+ goto IoControlEnd;
+
+ status = CreateClientHandle(NULL, clientEntry, &ret->ClientEntryHandle);
+ KphDereferenceObject(clientEntry);
+ retLength = sizeof(*ret);
+ }
+ break;
+
+ /* SsCreateRuleSetEntry
+ *
+ * Creates a system service logging ruleset entry.
+ */
+ case KPH_SSCREATERULESETENTRY:
+ {
+ struct
+ {
+ HANDLE ClientEntryHandle;
+ KPHSS_FILTER_TYPE DefaultFilterType;
+ KPHSS_RULESET_ACTION Action;
+ } *args = dataBuffer;
+ struct
+ {
+ HANDLE RuleSetEntryHandle;
+ } *ret = dataBuffer;
+ PKPHSS_CLIENT_ENTRY clientEntry;
+ PKPHSS_RULESET_ENTRY ruleSetEntry;
+
+ CHECK_IN_OUT_LENGTH;
+
+ /* Reference the client entry. */
+ status = ReferenceClientHandle(
+ NULL,
+ args->ClientEntryHandle,
+ KphSsClientEntryType,
+ &clientEntry
+ );
+
+ if (!NT_SUCCESS(status))
+ goto IoControlEnd;
+
+ /* Create the ruleset entry. */
+ status = KphSsCreateRuleSetEntry(
+ &ruleSetEntry,
+ clientEntry,
+ args->DefaultFilterType,
+ args->Action
+ );
+ KphDereferenceObject(clientEntry);
+
+ if (!NT_SUCCESS(status))
+ goto IoControlEnd;
+
+ /* Create and return a handle to the ruleset entry. */
+ status = CreateClientHandle(NULL, ruleSetEntry, &ret->RuleSetEntryHandle);
+ KphDereferenceObject(ruleSetEntry);
+ retLength = sizeof(*ret);
+ }
+ break;
+
+ /* SsRemoveRule
+ *
+ * Removes a rule from a ruleset.
+ */
+ case KPH_SSREMOVERULE:
+ {
+ struct
+ {
+ HANDLE RuleSetEntryHandle;
+ HANDLE RuleEntryHandle;
+ } *args = dataBuffer;
+ PKPHSS_RULESET_ENTRY ruleSetEntry;
+
+ CHECK_IN_LENGTH;
+
+ /* Reference the ruleset entry. */
+ status = ReferenceClientHandle(
+ NULL,
+ args->RuleSetEntryHandle,
+ KphSsRuleSetEntryType,
+ &ruleSetEntry
+ );
+
+ if (!NT_SUCCESS(status))
+ goto IoControlEnd;
+
+ /* Remove the rule. */
+ status = KphSsRemoveRule(ruleSetEntry, args->RuleEntryHandle);
+ KphDereferenceObject(ruleSetEntry);
+ }
+ break;
+
+ /* SsAddProcessIdRule
+ *
+ * Adds a process ID rule to a ruleset.
+ */
+ case KPH_SSADDPROCESSIDRULE:
+ {
+ struct
+ {
+ HANDLE RuleSetEntryHandle;
+ KPHSS_FILTER_TYPE FilterType;
+ HANDLE ProcessId;
+ } *args = dataBuffer;
+ struct
+ {
+ HANDLE RuleEntryHandle;
+ } *ret = dataBuffer;
+ PKPHSS_RULESET_ENTRY ruleSetEntry;
+ PKPHSS_RULE_ENTRY ruleEntry;
+
+ CHECK_IN_OUT_LENGTH;
+
+ /* Reference the client entry. */
+ status = ReferenceClientHandle(
+ NULL,
+ args->RuleSetEntryHandle,
+ KphSsRuleSetEntryType,
+ &ruleSetEntry
+ );
+
+ if (!NT_SUCCESS(status))
+ goto IoControlEnd;
+
+ /* Add a process ID rule. */
+ status = KphSsAddProcessIdRule(
+ &ruleEntry,
+ ruleSetEntry,
+ args->FilterType,
+ args->ProcessId
+ );
+ KphDereferenceObject(ruleSetEntry);
+
+ if (!NT_SUCCESS(status))
+ goto IoControlEnd;
+
+ /* Return the rule handle. */
+ ret->RuleEntryHandle = KphSsGetHandleRule(ruleEntry);
+ KphDereferenceObject(ruleEntry);
+ retLength = sizeof(*ret);
+ }
+ break;
+
+ /* SsAddThreadIdRule
+ *
+ * Adds a thread ID rule to a ruleset.
+ */
+ case KPH_SSADDTHREADIDRULE:
+ {
+ struct
+ {
+ HANDLE RuleSetEntryHandle;
+ KPHSS_FILTER_TYPE FilterType;
+ HANDLE ThreadId;
+ } *args = dataBuffer;
+ struct
+ {
+ HANDLE RuleEntryHandle;
+ } *ret = dataBuffer;
+ PKPHSS_RULESET_ENTRY ruleSetEntry;
+ PKPHSS_RULE_ENTRY ruleEntry;
+
+ CHECK_IN_OUT_LENGTH;
+
+ /* Reference the client entry. */
+ status = ReferenceClientHandle(
+ NULL,
+ args->RuleSetEntryHandle,
+ KphSsRuleSetEntryType,
+ &ruleSetEntry
+ );
+
+ if (!NT_SUCCESS(status))
+ goto IoControlEnd;
+
+ /* Add a thread ID rule. */
+ status = KphSsAddThreadIdRule(
+ &ruleEntry,
+ ruleSetEntry,
+ args->FilterType,
+ args->ThreadId
+ );
+ KphDereferenceObject(ruleSetEntry);
+
+ if (!NT_SUCCESS(status))
+ goto IoControlEnd;
+
+ /* Return the rule handle. */
+ ret->RuleEntryHandle = KphSsGetHandleRule(ruleEntry);
+ KphDereferenceObject(ruleEntry);
+ retLength = sizeof(*ret);
+ }
+ break;
+
+ /* SsAddPreviousModeRule
+ *
+ * Adds a previous mode rule to a ruleset.
+ */
+ case KPH_SSADDPREVIOUSMODERULE:
+ {
+ struct
+ {
+ HANDLE RuleSetEntryHandle;
+ KPHSS_FILTER_TYPE FilterType;
+ KPROCESSOR_MODE PreviousMode;
+ } *args = dataBuffer;
+ struct
+ {
+ HANDLE RuleEntryHandle;
+ } *ret = dataBuffer;
+ PKPHSS_RULESET_ENTRY ruleSetEntry;
+ PKPHSS_RULE_ENTRY ruleEntry;
+
+ CHECK_IN_OUT_LENGTH;
+
+ /* Reference the client entry. */
+ status = ReferenceClientHandle(
+ NULL,
+ args->RuleSetEntryHandle,
+ KphSsRuleSetEntryType,
+ &ruleSetEntry
+ );
+
+ if (!NT_SUCCESS(status))
+ goto IoControlEnd;
+
+ /* Add a previous mode rule. */
+ status = KphSsAddPreviousModeRule(
+ &ruleEntry,
+ ruleSetEntry,
+ args->FilterType,
+ args->PreviousMode
+ );
+ KphDereferenceObject(ruleSetEntry);
+
+ if (!NT_SUCCESS(status))
+ goto IoControlEnd;
+
+ /* Return the rule handle. */
+ ret->RuleEntryHandle = KphSsGetHandleRule(ruleEntry);
+ KphDereferenceObject(ruleEntry);
+ retLength = sizeof(*ret);
+ }
+ break;
+
+ /* SsAddNumberRule
+ *
+ * Adds a system service number rule to a ruleset.
+ */
+ case KPH_SSADDNUMBERRULE:
+ {
+ struct
+ {
+ HANDLE RuleSetEntryHandle;
+ KPHSS_FILTER_TYPE FilterType;
+ ULONG Number;
+ } *args = dataBuffer;
+ struct
+ {
+ HANDLE RuleEntryHandle;
+ } *ret = dataBuffer;
+ PKPHSS_RULESET_ENTRY ruleSetEntry;
+ PKPHSS_RULE_ENTRY ruleEntry;
+
+ CHECK_IN_OUT_LENGTH;
+
+ /* Reference the client entry. */
+ status = ReferenceClientHandle(
+ NULL,
+ args->RuleSetEntryHandle,
+ KphSsRuleSetEntryType,
+ &ruleSetEntry
+ );
+
+ if (!NT_SUCCESS(status))
+ goto IoControlEnd;
+
+ /* Add a number rule. */
+ status = KphSsAddNumberRule(
+ &ruleEntry,
+ ruleSetEntry,
+ args->FilterType,
+ args->Number
+ );
+ KphDereferenceObject(ruleSetEntry);
+
+ if (!NT_SUCCESS(status))
+ goto IoControlEnd;
+
+ /* Return the rule handle. */
+ ret->RuleEntryHandle = KphSsGetHandleRule(ruleEntry);
+ KphDereferenceObject(ruleEntry);
+ retLength = sizeof(*ret);
+ }
+ break;
+
+ /* SsEnableClientEntry
+ *
+ * Enables or disables a client entry.
+ */
+ case KPH_SSENABLECLIENTENTRY:
+ {
+ struct
+ {
+ HANDLE ClientEntryHandle;
+ BOOLEAN Enable;
+ } *args = dataBuffer;
+ PKPHSS_CLIENT_ENTRY clientEntry;
+
+ CHECK_IN_LENGTH;
+
+ /* Reference the client entry. */
+ status = ReferenceClientHandle(
+ NULL,
+ args->ClientEntryHandle,
+ KphSsClientEntryType,
+ &clientEntry
+ );
+
+ if (!NT_SUCCESS(status))
+ goto IoControlEnd;
+
+ /* Enable/disable the client entry. */
+ status = KphSsEnableClientEntry(clientEntry, args->Enable);
+ KphDereferenceObject(clientEntry);
+ }
+ break;
+
+ default:
+ {
+ dprintf("Unrecognized IOCTL code 0x%08x\n", controlCode);
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ }
+ break;
+ }
+
+ /* Restore the old packing. */
+ #include
+
+IoControlEnd:
+ Irp->IoStatus.Information = retLength;
+ Irp->IoStatus.Status = status;
+ dprintf("IOCTL 0x%08x result was 0x%08x\n", controlCode, status);
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+NTSTATUS KphDispatchRead(PDEVICE_OBJECT DeviceObject, PIRP Irp)
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PIO_STACK_LOCATION ioStackIrp = NULL;
+ ULONG retLength = 0;
+
+ ioStackIrp = IoGetCurrentIrpStackLocation(Irp);
+
+ if (ioStackIrp != NULL)
+ {
+ PCHAR readDataBuffer = (PCHAR)Irp->AssociatedIrp.SystemBuffer;
+ ULONG readLength = ioStackIrp->Parameters.Read.Length;
+
+ if (readDataBuffer != NULL)
+ {
+ dprintf("Client read %d bytes!\n", readLength);
+
+ if (readLength == 4)
+ {
+ *(ULONG *)readDataBuffer = KPH_CTL_CODE(0);
+ retLength = 4;
+ }
+ else
+ {
+ status = STATUS_INFO_LENGTH_MISMATCH;
+ }
+ }
+ }
+
+ Irp->IoStatus.Information = retLength;
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+NTSTATUS KphUnsupported(PDEVICE_OBJECT DeviceObject, PIRP Irp)
+{
+ dfprintf("Unsupported function called.\n");
+
+ return STATUS_NOT_SUPPORTED;
+}
diff --git a/branches/ph-plugins/KProcessHacker/makefile b/branches/ph-plugins/KProcessHacker/makefile
new file mode 100644
index 000000000..05a507be4
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/makefile
@@ -0,0 +1 @@
+!INCLUDE $(NTMAKEENV)\makefile.def
\ No newline at end of file
diff --git a/branches/ph-plugins/KProcessHacker/mm.c b/branches/ph-plugins/KProcessHacker/mm.c
new file mode 100644
index 000000000..84492ab34
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/mm.c
@@ -0,0 +1,703 @@
+/*
+ * Process Hacker Driver -
+ * memory manager
+ *
+ * Copyright (C) 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 .
+ */
+
+#include "include/kph.h"
+#include "include/mm.h"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, KphReadVirtualMemory)
+#pragma alloc_text(PAGE, KphUnsafeReadVirtualMemory)
+#pragma alloc_text(PAGE, KphWriteVirtualMemory)
+#pragma alloc_text(PAGE, MiDoMappedCopy)
+#pragma alloc_text(PAGE, MiDoPoolCopy)
+#pragma alloc_text(PAGE, MiGetExceptionInfo)
+#pragma alloc_text(PAGE, MmCopyVirtualMemory)
+#endif
+
+/* KphReadVirtualMemory
+ *
+ * Reads virtual memory from the specified process.
+ */
+NTSTATUS KphReadVirtualMemory(
+ __in HANDLE ProcessHandle,
+ __in PVOID BaseAddress,
+ __out_bcount(BufferLength) PVOID Buffer,
+ __in ULONG BufferLength,
+ __out_opt PULONG ReturnLength,
+ __in KPROCESSOR_MODE AccessMode
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PEPROCESS processObject;
+ ULONG returnLength = 0;
+
+ /* Probe user input if we're not from kernel-mode. */
+ if (AccessMode != KernelMode)
+ {
+ if ((((ULONG_PTR)BaseAddress + BufferLength) < (ULONG_PTR)BaseAddress) ||
+ (((ULONG_PTR)Buffer + BufferLength) < (ULONG_PTR)Buffer) ||
+ (((ULONG_PTR)BaseAddress + BufferLength) > (ULONG_PTR)MmHighestUserAddress) ||
+ (((ULONG_PTR)Buffer + BufferLength) > (ULONG_PTR)MmHighestUserAddress))
+ {
+ return STATUS_ACCESS_VIOLATION;
+ }
+
+ __try
+ {
+ if (ReturnLength)
+ ProbeForWrite(ReturnLength, sizeof(ULONG), 1);
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ return STATUS_ACCESS_VIOLATION;
+ }
+ }
+
+ /* If we actually have work to do, reference the process object and
+ call the internal function. */
+ if (BufferLength)
+ {
+ status = ObReferenceObjectByHandle(
+ ProcessHandle,
+ PROCESS_VM_READ,
+ *PsProcessType,
+ KernelMode,
+ &processObject,
+ NULL
+ );
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ status = MmCopyVirtualMemory(
+ processObject,
+ BaseAddress,
+ PsGetCurrentProcess(),
+ Buffer,
+ BufferLength,
+ AccessMode,
+ &returnLength
+ );
+ ObDereferenceObject(processObject);
+ }
+
+ if (ReturnLength)
+ {
+ __try
+ {
+ *ReturnLength = returnLength;
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ status = GetExceptionCode();
+ }
+ }
+
+ return status;
+}
+
+NTSTATUS KphUnsafeReadVirtualMemory(
+ __in HANDLE ProcessHandle,
+ __in PVOID BaseAddress,
+ __out_bcount(BufferLength) PVOID Buffer,
+ __in ULONG BufferLength,
+ __out_opt PULONG ReturnLength,
+ __in KPROCESSOR_MODE AccessMode
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ ULONG returnLength = 0;
+
+ /* Initial probing. */
+ if (AccessMode != KernelMode)
+ {
+ if ((((ULONG_PTR)BaseAddress + BufferLength) < (ULONG_PTR)BaseAddress) ||
+ (((ULONG_PTR)Buffer + BufferLength) < (ULONG_PTR)Buffer) ||
+ (((ULONG_PTR)Buffer + BufferLength) > (ULONG_PTR)MmHighestUserAddress))
+ {
+ return STATUS_ACCESS_VIOLATION;
+ }
+
+ __try
+ {
+ ProbeForWrite(Buffer, BufferLength, 1);
+
+ if (ReturnLength)
+ ProbeForWrite(ReturnLength, sizeof(ULONG), 1);
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ return GetExceptionCode();
+ }
+ }
+
+ /* Make sure we have something to copy. */
+ if (BufferLength == 0)
+ {
+ __try
+ {
+ *ReturnLength = 0;
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ return GetExceptionCode();
+ }
+
+ return STATUS_SUCCESS;
+ }
+
+ /* Select the appropriate copy method. */
+ if (((ULONG_PTR)BaseAddress + BufferLength) > (ULONG_PTR)MmHighestUserAddress)
+ {
+ /* Kernel memory unsafe copy. */
+
+ __try
+ {
+ /* Probe the address range. */
+ KphProbeSystemAddressRange(BaseAddress, BufferLength);
+
+ /* Copy the data. */
+ memcpy(Buffer, BaseAddress, BufferLength);
+ returnLength = BufferLength;
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ status = GetExceptionCode();
+ }
+
+ if (ReturnLength)
+ {
+ __try
+ {
+ *ReturnLength = returnLength;
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ status = GetExceptionCode();
+ }
+ }
+ }
+ else
+ {
+ /* User memory safe copy. */
+ status = KphReadVirtualMemory(
+ ProcessHandle,
+ BaseAddress,
+ Buffer,
+ BufferLength,
+ ReturnLength,
+ AccessMode
+ );
+ }
+
+ return status;
+}
+
+/* KphWriteVirtualMemory
+ *
+ * Writes virtual memory to the specified process.
+ */
+NTSTATUS KphWriteVirtualMemory(
+ __in HANDLE ProcessHandle,
+ __in PVOID BaseAddress,
+ __in_bcount(BufferLength) PVOID Buffer,
+ __in ULONG BufferLength,
+ __out_opt PULONG ReturnLength,
+ __in KPROCESSOR_MODE AccessMode
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PEPROCESS processObject;
+ ULONG returnLength = 0;
+
+ /* Probe user input if we're not from kernel-mode. */
+ if (AccessMode != KernelMode)
+ {
+ if ((((ULONG_PTR)BaseAddress + BufferLength) < (ULONG_PTR)BaseAddress) ||
+ (((ULONG_PTR)Buffer + BufferLength) < (ULONG_PTR)Buffer) ||
+ (((ULONG_PTR)BaseAddress + BufferLength) > (ULONG_PTR)MmHighestUserAddress) ||
+ (((ULONG_PTR)Buffer + BufferLength) > (ULONG_PTR)MmHighestUserAddress))
+ {
+ return STATUS_ACCESS_VIOLATION;
+ }
+
+ __try
+ {
+ if (ReturnLength)
+ ProbeForWrite(ReturnLength, sizeof(ULONG), 1);
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ return STATUS_ACCESS_VIOLATION;
+ }
+ }
+
+ /* If we actually have work to do, reference the process object and
+ call the internal function. */
+ if (BufferLength)
+ {
+ status = ObReferenceObjectByHandle(
+ ProcessHandle,
+ PROCESS_VM_WRITE,
+ *PsProcessType,
+ KernelMode,
+ &processObject,
+ NULL
+ );
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ status = MmCopyVirtualMemory(
+ PsGetCurrentProcess(),
+ Buffer,
+ processObject,
+ BaseAddress,
+ BufferLength,
+ AccessMode,
+ &returnLength
+ );
+ ObDereferenceObject(processObject);
+ }
+
+ if (ReturnLength)
+ {
+ __try
+ {
+ *ReturnLength = returnLength;
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ status = GetExceptionCode();
+ }
+ }
+
+ return status;
+}
+
+/* MiDoMappedCopy
+ *
+ * Copies virtual memory from the source process to the target process
+ * using a memory mapping.
+ */
+NTSTATUS MiDoMappedCopy(
+ __in PEPROCESS FromProcess,
+ __in PVOID FromAddress,
+ __in PEPROCESS ToProcess,
+ __in PVOID ToAddress,
+ __in ULONG BufferLength,
+ __in KPROCESSOR_MODE AccessMode,
+ __out PULONG ReturnLength
+ )
+{
+ PFN_NUMBER mdlBuffer[(sizeof(MDL) / sizeof(PFN_NUMBER)) + MI_MAPPED_COPY_PAGES + 1];
+ PMDL mdl = (PMDL)mdlBuffer;
+ /* The mapped address. */
+ PVOID mappedAddress;
+ /* The total size allocated (mapped pages). */
+ ULONG totalSize;
+ /* The block size. */
+ ULONG blockSize;
+ /* The amount still left to copy. */
+ ULONG stillToCopy;
+ /* Attach state. */
+ KPH_ATTACH_STATE attachState;
+ /* The current source address. */
+ PVOID sourceAddress;
+ /* The current target address. */
+ PVOID targetAddress;
+ /* Whether the pages have been locked. */
+ BOOLEAN pagesLocked;
+ /* Whether we are currently copying. */
+ BOOLEAN copying = FALSE;
+ /* Whether we are currently probing. */
+ BOOLEAN probing = FALSE;
+ /* Whether we are currently mapping. */
+ BOOLEAN mapping = FALSE;
+ /* Whether we have the bad address. */
+ BOOLEAN haveBadAddress;
+ /* The bad address of the exception. */
+ ULONG_PTR badAddress;
+
+ sourceAddress = FromAddress;
+ targetAddress = ToAddress;
+
+ totalSize = (MI_MAPPED_COPY_PAGES - 2) * PAGE_SIZE;
+
+ if (BufferLength <= totalSize)
+ totalSize = BufferLength;
+
+ stillToCopy = BufferLength;
+ blockSize = totalSize;
+
+ while (stillToCopy)
+ {
+ /* If we're at the last copy block, copy the remaining bytes instead
+ of the whole block size. */
+ if (stillToCopy < blockSize)
+ blockSize = stillToCopy;
+
+ /* Reset state. */
+ mappedAddress = NULL;
+ pagesLocked = FALSE;
+ copying = FALSE;
+
+ KphAttachProcess(FromProcess, &attachState);
+
+ __try
+ {
+ /* Probe only if this is the first time. */
+ if ((sourceAddress == FromAddress) && (AccessMode != KernelMode))
+ {
+ probing = TRUE;
+ ProbeForRead(sourceAddress, BufferLength, 1);
+ probing = FALSE;
+ }
+
+ /* Initialize the MDL. */
+ MmInitializeMdl(mdl, sourceAddress, blockSize);
+ MmProbeAndLockPages(mdl, AccessMode, IoReadAccess);
+ pagesLocked = TRUE;
+
+ /* Map the pages. */
+ mappedAddress = MmMapLockedPagesSpecifyCache(
+ mdl,
+ KernelMode,
+ MmCached,
+ NULL,
+ FALSE,
+ HighPagePriority
+ );
+
+ if (!mappedAddress)
+ {
+ /* Insufficient resources; exit. */
+ mapping = TRUE;
+ ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
+ }
+
+ KphDetachProcess(&attachState);
+
+ /* Attach to the target process and copy the mapped contents. */
+ KphAttachProcess(ToProcess, &attachState);
+
+ /* Probe only if this is the first time. */
+ if ((targetAddress == ToAddress) && (AccessMode != KernelMode))
+ {
+ probing = TRUE;
+ ProbeForWrite(targetAddress, BufferLength, 1);
+ probing = FALSE;
+ }
+
+ /* Copy the data. */
+ copying = TRUE;
+ memcpy(targetAddress, mappedAddress, blockSize);
+ }
+ __except (MiGetExceptionInfo(
+ GetExceptionInformation(),
+ &haveBadAddress,
+ &badAddress
+ ))
+ {
+ KphDetachProcess(&attachState);
+
+ /* If we mapped the pages, unmap them. */
+ if (mappedAddress)
+ MmUnmapLockedPages(mappedAddress, mdl);
+
+ /* If we locked the pages, unlock them. */
+ if (pagesLocked)
+ MmUnlockPages(mdl);
+
+ /* If we failed when probing or mapping, return the error code. */
+ if (probing || mapping)
+ return GetExceptionCode();
+
+ /* Otherwise, give the caller the number of bytes we copied. */
+ *ReturnLength = BufferLength - stillToCopy;
+
+ /* If we were copying, we can probably get the exact
+ number of bytes copied. */
+ if (copying && haveBadAddress)
+ *ReturnLength = (ULONG)(badAddress - (ULONG_PTR)sourceAddress);
+
+ return STATUS_PARTIAL_COPY;
+ }
+
+ KphDetachProcess(&attachState);
+ MmUnmapLockedPages(mappedAddress, mdl);
+ MmUnlockPages(mdl);
+
+ stillToCopy -= blockSize;
+ sourceAddress = (PVOID)((ULONG_PTR)sourceAddress + blockSize);
+ targetAddress = (PVOID)((ULONG_PTR)targetAddress + blockSize);
+ }
+
+ *ReturnLength = BufferLength;
+
+ return STATUS_SUCCESS;
+}
+
+/* MiDoPoolCopy
+ *
+ * Copies virtual memory from the source process to the target process
+ * using either a pool allocation or a stack buffer.
+ */
+NTSTATUS MiDoPoolCopy(
+ __in PEPROCESS FromProcess,
+ __in PVOID FromAddress,
+ __in PEPROCESS ToProcess,
+ __in PVOID ToAddress,
+ __in ULONG BufferLength,
+ __in KPROCESSOR_MODE AccessMode,
+ __out PULONG ReturnLength
+ )
+{
+ /* The size of the pool-allocated buffer. */
+ ULONG allocSize = MI_MAX_TRANSFER_SIZE;
+ /* The stack-based buffer. */
+ CHAR stackBuffer[MI_COPY_STACK_SIZE];
+ /* The buffer - could be from the pool or could be the stack buffer. */
+ PVOID buffer = NULL;
+ /* The block size - should be the same as the allocated size. */
+ ULONG blockSize;
+ /* The amount still left to copy. */
+ ULONG stillToCopy;
+ /* Attach state. */
+ KPH_ATTACH_STATE attachState;
+ /* The current source address. */
+ PVOID sourceAddress;
+ /* The current target address. */
+ PVOID targetAddress;
+ /* Whether we are currently copying. */
+ BOOLEAN copying = FALSE;
+ /* Whether we are currently probing. */
+ BOOLEAN probing = FALSE;
+ /* Whether we have the bad address. */
+ BOOLEAN haveBadAddress;
+ /* The bad address of the exception. */
+ ULONG_PTR badAddress;
+
+ sourceAddress = FromAddress;
+ targetAddress = ToAddress;
+
+ /* Don't allocate a buffer larger than the amount we're about to copy. */
+ if (allocSize > BufferLength)
+ allocSize = BufferLength;
+
+ /* If we're copying MI_COPY_STACK_SIZE bytes or less, use the stack buffer. */
+ if (BufferLength <= MI_COPY_STACK_SIZE)
+ {
+ buffer = stackBuffer;
+ }
+ else
+ {
+ /* Keep on trying to allocate a buffer, halving the size each time
+ we fail. */
+ while (TRUE)
+ {
+ buffer = ExAllocatePoolWithTag(NonPagedPool, allocSize, TAG_POOL_COPY);
+
+ /* Stop trying if we got a buffer. */
+ if (buffer)
+ break;
+
+ /* Otherwise, halve the size and try again. */
+ allocSize /= 2;
+ /* Could we use the stack buffer? */
+ if (allocSize <= MI_COPY_STACK_SIZE)
+ {
+ buffer = stackBuffer;
+ break;
+ }
+ }
+ }
+
+ stillToCopy = BufferLength;
+ blockSize = allocSize;
+
+ /* Perform the copy in blocks of blockSize. */
+ while (stillToCopy)
+ {
+ /* If we're at the last copy block, copy the remaining bytes instead
+ of the whole block size. */
+ if (stillToCopy < blockSize)
+ blockSize = stillToCopy;
+
+ copying = FALSE;
+ KphAttachProcess(FromProcess, &attachState);
+
+ __try
+ {
+ /* Probe before reading the source contents. */
+ /* Probe only if this is the first time. */
+ if ((sourceAddress == FromAddress) && (AccessMode != KernelMode))
+ {
+ probing = TRUE;
+ ProbeForRead(sourceAddress, BufferLength, 1);
+ probing = FALSE;
+ }
+
+ /* Copy the source contents to the buffer. */
+ memcpy(buffer, sourceAddress, blockSize);
+ KphDetachProcess(&attachState);
+
+ /* Probe before writing. */
+ KphAttachProcess(ToProcess, &attachState);
+
+ /* Probe only if this is the first time. */
+ if ((targetAddress == ToAddress) && (AccessMode != KernelMode))
+ {
+ probing = TRUE;
+ ProbeForWrite(targetAddress, BufferLength, 1);
+ probing = FALSE;
+ }
+
+ /* Copy the buffer contents to the destination. */
+ copying = TRUE;
+ memcpy(targetAddress, buffer, blockSize);
+ }
+ __except (MiGetExceptionInfo(
+ GetExceptionInformation(),
+ &haveBadAddress,
+ &badAddress
+ ))
+ {
+ KphDetachProcess(&attachState);
+
+ /* Free the allocated buffer if needed. */
+ if (buffer != stackBuffer)
+ ExFreePoolWithTag(buffer, TAG_POOL_COPY);
+
+ /* If we were probing an address, return the error code. */
+ if (probing)
+ return GetExceptionCode();
+
+ /* Otherwise, give the caller the number of bytes we copied. */
+ *ReturnLength = BufferLength - stillToCopy;
+
+ /* If we were copying, we can probably get the exact
+ number of bytes copied. */
+ if (copying && haveBadAddress)
+ *ReturnLength = (ULONG)(badAddress - (ULONG_PTR)sourceAddress);
+
+ return STATUS_PARTIAL_COPY;
+ }
+
+ KphDetachProcess(&attachState);
+
+ stillToCopy -= blockSize;
+ sourceAddress = (PVOID)((ULONG_PTR)sourceAddress + blockSize);
+ targetAddress = (PVOID)((ULONG_PTR)targetAddress + blockSize);
+ }
+
+ /* Free the buffer if it wasn't stack-allocated. */
+ if (buffer != stackBuffer)
+ ExFreePoolWithTag(buffer, TAG_POOL_COPY);
+
+ *ReturnLength = BufferLength;
+
+ return STATUS_SUCCESS;
+}
+
+ULONG MiGetExceptionInfo(
+ __in PEXCEPTION_POINTERS ExceptionInfo,
+ __out PBOOLEAN HaveBadAddress,
+ __out PULONG_PTR BadAddress
+ )
+{
+ PEXCEPTION_RECORD exceptionRecord;
+
+ *HaveBadAddress = FALSE;
+ exceptionRecord = ExceptionInfo->ExceptionRecord;
+
+ if ((exceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) ||
+ (exceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) ||
+ (exceptionRecord->ExceptionCode == STATUS_IN_PAGE_ERROR))
+ {
+ if (exceptionRecord->NumberParameters > 1)
+ {
+ /* We have the address. */
+ *HaveBadAddress = TRUE;
+ *BadAddress = exceptionRecord->ExceptionInformation[1];
+ }
+ }
+
+ return EXCEPTION_EXECUTE_HANDLER;
+}
+
+NTSTATUS MmCopyVirtualMemory(
+ __in PEPROCESS FromProcess,
+ __in PVOID FromAddress,
+ __in PEPROCESS ToProcess,
+ __in PVOID ToAddress,
+ __in ULONG BufferLength,
+ __in KPROCESSOR_MODE AccessMode,
+ __out PULONG ReturnLength
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PEPROCESS processToLock = FromProcess;
+
+ if (!BufferLength)
+ return STATUS_SUCCESS;
+
+ /* If we're copying from the current process, lock the target. */
+ if (processToLock == PsGetCurrentProcess())
+ processToLock = ToProcess;
+
+ /* Prevent the process from terminating. */
+ if (!KphAcquireProcessRundownProtection(processToLock))
+ return STATUS_PROCESS_IS_TERMINATING;
+
+ /* If the amount we're trying to copy is over the threshold
+ for MiDoPoolCopy, use MiDoMappedCopy. */
+ if (BufferLength > MM_POOL_COPY_THRESHOLD)
+ {
+ status = MiDoMappedCopy(
+ FromProcess,
+ FromAddress,
+ ToProcess,
+ ToAddress,
+ BufferLength,
+ AccessMode,
+ ReturnLength
+ );
+ }
+ else
+ {
+ status = MiDoPoolCopy(
+ FromProcess,
+ FromAddress,
+ ToProcess,
+ ToAddress,
+ BufferLength,
+ AccessMode,
+ ReturnLength
+ );
+ }
+
+ /* Allow the process to terminate. */
+ KphReleaseProcessRundownProtection(processToLock);
+
+ return status;
+}
diff --git a/branches/ph-plugins/KProcessHacker/ob.c b/branches/ph-plugins/KProcessHacker/ob.c
new file mode 100644
index 000000000..21255b0e8
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/ob.c
@@ -0,0 +1,847 @@
+/*
+ * Process Hacker Driver -
+ * object manager
+ *
+ * Copyright (C) 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 .
+ */
+
+#include "include/kph.h"
+#include "include/ob.h"
+
+BOOLEAN KphpQueryProcessHandlesEnumCallback(
+ __inout PHANDLE_TABLE_ENTRY HandleTableEntry,
+ __in HANDLE Handle,
+ __in POBP_QUERY_PROCESS_HANDLES_DATA Context
+ );
+
+BOOLEAN KphpSetHandleGrantedAccessEnumCallback(
+ __inout PHANDLE_TABLE_ENTRY HandleTableEntry,
+ __in HANDLE Handle,
+ __in POBP_SET_HANDLE_GRANTED_ACCESS_DATA Context
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, KphDuplicateObject)
+#pragma alloc_text(PAGE, ObDuplicateObject)
+#endif
+
+/* This attribute is now stored in the GrantedAccess field. */
+ULONG ObpAccessProtectCloseBit = 0x80000000;
+
+/* KphDuplicateObject
+ *
+ * Duplicates a handle from the source process to the target process.
+ */
+NTSTATUS KphDuplicateObject(
+ __in HANDLE SourceProcessHandle,
+ __in HANDLE SourceHandle,
+ __in_opt HANDLE TargetProcessHandle,
+ __out_opt PHANDLE TargetHandle,
+ __in ACCESS_MASK DesiredAccess,
+ __in ULONG HandleAttributes,
+ __in ULONG Options,
+ __in KPROCESSOR_MODE AccessMode
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PEPROCESS sourceProcess = NULL;
+ PEPROCESS targetProcess = NULL;
+ HANDLE targetHandle;
+
+ if (TargetHandle && AccessMode != KernelMode)
+ {
+ __try
+ {
+ ProbeForWrite(TargetHandle, sizeof(HANDLE), 1);
+ *TargetHandle = NULL;
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ return STATUS_ACCESS_VIOLATION;
+ }
+ }
+
+ status = ObReferenceObjectByHandle(
+ SourceProcessHandle,
+ PROCESS_DUP_HANDLE,
+ *PsProcessType,
+ KernelMode,
+ &sourceProcess,
+ NULL
+ );
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ /* Target handle is optional. */
+ if (TargetProcessHandle)
+ {
+ status = ObReferenceObjectByHandle(
+ TargetProcessHandle,
+ PROCESS_DUP_HANDLE,
+ *PsProcessType,
+ KernelMode,
+ &targetProcess,
+ NULL
+ );
+
+ if (!NT_SUCCESS(status))
+ return status;
+ }
+
+ /* Fix the source handle if the source process is
+ * the system process.
+ */
+ if (sourceProcess == PsInitialSystemProcess)
+ MakeKernelHandle(SourceHandle);
+
+ /* Call the internal function. */
+ status = ObDuplicateObject(
+ sourceProcess,
+ targetProcess,
+ SourceHandle,
+ &targetHandle,
+ DesiredAccess,
+ HandleAttributes,
+ Options,
+ AccessMode
+ );
+
+ if (TargetHandle)
+ {
+ __try
+ {
+ *TargetHandle = targetHandle;
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ status = STATUS_ACCESS_VIOLATION;
+ }
+ }
+
+ ObDereferenceObject(sourceProcess);
+ if (targetProcess)
+ ObDereferenceObject(targetProcess);
+
+ return status;
+}
+
+/* KphEnumProcessHandleTable
+ *
+ * Enumerates the handles in the specified process' handle table.
+ */
+BOOLEAN KphEnumProcessHandleTable(
+ __in PEPROCESS Process,
+ __in PEX_ENUM_HANDLE_CALLBACK EnumHandleProcedure,
+ __inout PVOID Context,
+ __out_opt PHANDLE Handle
+ )
+{
+ BOOLEAN result = FALSE;
+ PHANDLE_TABLE handleTable = NULL;
+
+ handleTable = ObReferenceProcessHandleTable(Process);
+
+ if (!handleTable)
+ return FALSE;
+
+ result = ExEnumHandleTable(
+ handleTable,
+ EnumHandleProcedure,
+ Context,
+ Handle
+ );
+ ObDereferenceProcessHandleTable(Process);
+
+ return result;
+}
+
+/* KphGetObjectTypeNt
+ *
+ * Gets the type of an object.
+ */
+POBJECT_TYPE KphGetObjectTypeNt(
+ __in PVOID Object
+ )
+{
+ /* XP to Vista: A pointer to the object type is
+ * stored in the object header.
+ */
+ if (
+ WindowsVersion >= WINDOWS_XP &&
+ WindowsVersion <= WINDOWS_VISTA
+ )
+ {
+ return OBJECT_TO_OBJECT_HEADER(Object)->Type;
+ }
+ /* Seven and above: An index to an internal object type
+ * table is stored in the object header. Luckily we have
+ * a new exported function, ObGetObjectType, to get
+ * the object type.
+ */
+ else if (WindowsVersion >= WINDOWS_7)
+ {
+ return ObGetObjectType(Object);
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+/* KphOpenDirectoryObject
+ *
+ * Opens a directory object.
+ */
+NTSTATUS KphOpenDirectoryObject(
+ __out PHANDLE DirectoryObjectHandle,
+ __in ACCESS_MASK DesiredAccess,
+ __in POBJECT_ATTRIBUTES ObjectAttributes,
+ __in KPROCESSOR_MODE AccessMode
+ )
+{
+ return KphOpenNamedObject(
+ DirectoryObjectHandle,
+ DesiredAccess,
+ ObjectAttributes,
+ NULL,
+ AccessMode
+ );
+}
+
+/* KphOpenNamedObject
+ *
+ * Opens a named object.
+ */
+NTSTATUS KphOpenNamedObject(
+ __out PHANDLE ObjectHandle,
+ __in ACCESS_MASK DesiredAccess,
+ __in POBJECT_ATTRIBUTES ObjectAttributes,
+ __in POBJECT_TYPE ObjectType,
+ __in KPROCESSOR_MODE AccessMode
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ HANDLE objectHandle;
+ UNICODE_STRING capturedObjectName;
+ OBJECT_ATTRIBUTES objectAttributes = { 0 };
+
+ if (!ObjectAttributes)
+ return STATUS_INVALID_PARAMETER;
+
+ /* Probe user input. */
+ if (AccessMode != KernelMode)
+ {
+ __try
+ {
+ ProbeForWrite(ObjectHandle, sizeof(HANDLE), 1);
+ ProbeForRead(ObjectAttributes, sizeof(OBJECT_ATTRIBUTES), 1);
+
+ if (ObjectAttributes->ObjectName)
+ KphProbeForReadUnicodeString(ObjectAttributes->ObjectName);
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ return GetExceptionCode();
+ }
+ }
+
+ __try
+ {
+ /* Verify parameters. */
+ if (!ObjectAttributes->ObjectName)
+ return STATUS_INVALID_PARAMETER;
+
+ /* Copy the object attributes structure. */
+ memcpy(&objectAttributes, ObjectAttributes, sizeof(OBJECT_ATTRIBUTES));
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ return GetExceptionCode();
+ }
+
+ /* Capture the ObjectName string. */
+ status = KphCaptureUnicodeString(
+ ObjectAttributes->ObjectName,
+ &capturedObjectName
+ );
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ /* Set the new string in the object attributes. */
+ objectAttributes.ObjectName = &capturedObjectName;
+ /* Make sure the SecurityDescriptor and SecurityQualityOfService fields are NULL
+ * since we haven't probed them.
+ */
+ objectAttributes.SecurityDescriptor = NULL;
+ objectAttributes.SecurityQualityOfService = NULL;
+
+ /* Open the object. */
+ status = ObOpenObjectByName(
+ &objectAttributes,
+ ObjectType,
+ KernelMode,
+ NULL,
+ DesiredAccess,
+ NULL,
+ &objectHandle
+ );
+
+ /* Free the captured ObjectName. */
+ KphFreeCapturedUnicodeString(&capturedObjectName);
+
+ /* Pass the handle back. */
+ __try
+ {
+ *ObjectHandle = objectHandle;
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ status = GetExceptionCode();
+ }
+
+ return status;
+}
+
+/* KphQueryFileObjectName
+ *
+ * Queries the name of a file object.
+ *
+ * Technique from YAPM.
+ */
+NTSTATUS KphQueryNameFileObject(
+ __in PFILE_OBJECT FileObject,
+ __inout_bcount(BufferLength) PUNICODE_STRING Buffer,
+ __in ULONG BufferLength,
+ __out PULONG ReturnLength
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ ULONG returnLength;
+ PCHAR objectName;
+ ULONG usedLength;
+ ULONG subNameLength;
+ PFILE_OBJECT relatedFileObject;
+
+ /* We need at least the size of UNICODE_STRING to
+ * continue.
+ */
+ if (BufferLength < sizeof(UNICODE_STRING))
+ {
+ *ReturnLength = sizeof(UNICODE_STRING);
+
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ /* Assume failure. */
+ Buffer->Length = 0;
+ /* We will place the object name directly after the
+ * UNICODE_STRING structure in the buffer.
+ */
+ Buffer->Buffer = (PWSTR)PTR_ADD_OFFSET(Buffer, sizeof(UNICODE_STRING));
+ /* Retain a local pointer to the object name so we
+ * can manipulate the pointer.
+ */
+ objectName = (PCHAR)Buffer->Buffer;
+ /* A variable that keeps track of how much space we
+ * have used.
+ */
+ usedLength = sizeof(UNICODE_STRING);
+
+ /* Check if the file object has an associated device
+ * (e.g. "\Device\NamedPipe", "\Device\Mup"). We can
+ * use the user-supplied buffer for this since if the
+ * buffer isn't big enough, we can't proceed anyway
+ * (we are going to use the name).
+ */
+ if (FileObject->DeviceObject)
+ {
+ status = ObQueryNameString(
+ FileObject->DeviceObject,
+ (POBJECT_NAME_INFORMATION)Buffer,
+ BufferLength,
+ &returnLength
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ *ReturnLength = returnLength;
+
+ return status;
+ }
+
+ /* The UNICODE_STRING in the buffer is now filled in.
+ * We will append to the object name later, so
+ * we need to fix the object name pointer by adding
+ * the length, in bytes, of the device name string we
+ * just got.
+ */
+ objectName += Buffer->Length;
+ usedLength += Buffer->Length;
+ }
+
+ /* Check if the file object has a file name component. If not,
+ * we can't do anything else, so we just return the name we
+ * have already.
+ */
+ if (!FileObject->FileName.Buffer)
+ {
+ *ReturnLength = usedLength;
+
+ return STATUS_SUCCESS;
+ }
+
+ /* The file object has a name. We need to walk up the file
+ * object tree and append the names of the related file
+ * objects in reverse order. This means we need to calculate
+ * the total length first.
+ */
+
+ relatedFileObject = FileObject;
+ subNameLength = 0;
+
+ do
+ {
+ subNameLength += relatedFileObject->FileName.Length;
+
+ /* Avoid infinite loops. */
+ if (relatedFileObject == relatedFileObject->RelatedFileObject)
+ break;
+
+ relatedFileObject = relatedFileObject->RelatedFileObject;
+ }
+ while (relatedFileObject);
+
+ usedLength += subNameLength;
+
+ /* Check if we have enough space to write the whole thing. */
+ if (usedLength > BufferLength)
+ {
+ *ReturnLength = usedLength;
+
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ /* We're ready to begin copying the names. */
+
+ /* Add the name length because we're copying in reverse order. */
+ objectName += subNameLength;
+
+ relatedFileObject = FileObject;
+
+ do
+ {
+ objectName -= relatedFileObject->FileName.Length;
+ memcpy(objectName, relatedFileObject->FileName.Buffer, relatedFileObject->FileName.Length);
+
+ /* Avoid infinite loops. */
+ if (relatedFileObject == relatedFileObject->RelatedFileObject)
+ break;
+
+ relatedFileObject = relatedFileObject->RelatedFileObject;
+ }
+ while (relatedFileObject);
+
+ /* Update the length. */
+ Buffer->Length += (USHORT)subNameLength;
+
+ /* Pass the return length back. */
+ *ReturnLength = usedLength;
+
+ return STATUS_SUCCESS;
+}
+
+/* KphQueryObjectName
+ *
+ * Queries the name of an object.
+ */
+NTSTATUS KphQueryNameObject(
+ __in PVOID Object,
+ __inout_bcount(BufferLength) PUNICODE_STRING Buffer,
+ __in ULONG BufferLength,
+ __out PULONG ReturnLength
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ POBJECT_TYPE objectType;
+
+ objectType = KphGetObjectTypeNt(Object);
+
+ /* Check if we are going to hang when querying the object, and use
+ * the special file object query function if needed.
+ */
+ if (
+ (objectType == *IoFileObjectType) &&
+ (((PFILE_OBJECT)Object)->Busy || ((PFILE_OBJECT)Object)->Waiters)
+ )
+ {
+ status = KphQueryNameFileObject((PFILE_OBJECT)Object, Buffer, BufferLength, ReturnLength);
+ }
+ else
+ {
+ status = ObQueryNameString(Object, (POBJECT_NAME_INFORMATION)Buffer, BufferLength, ReturnLength);
+ }
+
+ return status;
+}
+
+/* KphQueryProcessHandles
+ *
+ * Queries a process handle table.
+ */
+NTSTATUS KphQueryProcessHandles(
+ __in HANDLE ProcessHandle,
+ __out_bcount_opt(BufferLength) PPROCESS_HANDLE_INFORMATION Buffer,
+ __in_opt ULONG BufferLength,
+ __out_opt PULONG ReturnLength,
+ __in KPROCESSOR_MODE AccessMode
+ )
+{
+ NTSTATUS status;
+ BOOLEAN result;
+ PEPROCESS processObject;
+ OBP_QUERY_PROCESS_HANDLES_DATA context;
+
+ /* Probe buffer contents. */
+ if (AccessMode != KernelMode)
+ {
+ __try
+ {
+ if (Buffer)
+ ProbeForWrite(Buffer, BufferLength, 1);
+ if (ReturnLength)
+ ProbeForWrite(ReturnLength, sizeof(ULONG), 1);
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ return GetExceptionCode();
+ }
+ }
+
+ /* Reference the process object. */
+ status = ObReferenceObjectByHandle(
+ ProcessHandle,
+ PROCESS_QUERY_INFORMATION,
+ *PsProcessType,
+ KernelMode,
+ &processObject,
+ NULL
+ );
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ /* Initialize the enumeration context. */
+ context.Buffer = Buffer;
+ context.BufferLength = BufferLength;
+ context.CurrentIndex = 0;
+ context.Status = STATUS_SUCCESS;
+
+ /* Enumerate the handles. */
+ result = KphEnumProcessHandleTable(
+ processObject,
+ KphpQueryProcessHandlesEnumCallback,
+ &context,
+ NULL
+ );
+ ObDereferenceObject(processObject);
+
+ /* Write the number of handles (if we have a buffer). */
+ if (
+ Buffer &&
+ BufferLength >= sizeof(ULONG)
+ )
+ {
+ __try
+ {
+ Buffer->HandleCount = context.CurrentIndex;
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ return GetExceptionCode();
+ }
+ }
+
+ /* Supply the return length if the caller wanted it. */
+ if (ReturnLength)
+ {
+ __try
+ {
+ /* CurrentIndex should contain the number of handles, so we simply multiply it
+ by the size of PROCESS_HANDLE. */
+ *ReturnLength = sizeof(ULONG) + context.CurrentIndex * sizeof(PROCESS_HANDLE);
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ return GetExceptionCode();
+ }
+ }
+
+ return context.Status;
+}
+
+/* KphpQueryProcessHandlesEnumCallback
+ *
+ * The callback for KphEnumProcessHandleTable, used by
+ * KphQueryProcessHandles.
+ */
+BOOLEAN KphpQueryProcessHandlesEnumCallback(
+ __inout PHANDLE_TABLE_ENTRY HandleTableEntry,
+ __in HANDLE Handle,
+ __in POBP_QUERY_PROCESS_HANDLES_DATA Context
+ )
+{
+ PROCESS_HANDLE handleInfo;
+ PPROCESS_HANDLE_INFORMATION buffer = Context->Buffer;
+ ULONG i;
+
+ handleInfo.Handle = Handle;
+ handleInfo.Object = ObpDecodeObject(HandleTableEntry->Object);
+ handleInfo.GrantedAccess = ObpDecodeGrantedAccess(HandleTableEntry->GrantedAccess);
+ handleInfo.HandleAttributes = ObpGetHandleAttributes(HandleTableEntry);
+
+ /* Increment the index regardless of whether the information will be written;
+ this will allow KphQueryProcessHandles to report the correct return length. */
+ i = Context->CurrentIndex++;
+
+ /* Only write if we have a buffer and have not exceeded the buffer length. */
+ if (
+ buffer &&
+ (sizeof(ULONG) + Context->CurrentIndex * sizeof(PROCESS_HANDLE)) <= Context->BufferLength
+ )
+ {
+ __try
+ {
+ buffer->Handles[i] = handleInfo;
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ /* Report an error. */
+ if (Context->Status == STATUS_SUCCESS)
+ Context->Status = GetExceptionCode();
+ }
+ }
+ else
+ {
+ /* Report that the buffer is too small. */
+ if (Context->Status == STATUS_SUCCESS)
+ Context->Status = STATUS_BUFFER_TOO_SMALL;
+ }
+
+ return FALSE;
+}
+
+/* KphSetHandleGrantedAccess
+ *
+ * Sets the granted access of a handle.
+ */
+NTSTATUS KphSetHandleGrantedAccess(
+ __in PEPROCESS Process,
+ __in HANDLE Handle,
+ __in ACCESS_MASK GrantedAccess
+ )
+{
+ BOOLEAN result;
+ OBP_SET_HANDLE_GRANTED_ACCESS_DATA context;
+
+ context.Handle = Handle;
+ context.GrantedAccess = GrantedAccess;
+
+ result = KphEnumProcessHandleTable(
+ Process,
+ KphpSetHandleGrantedAccessEnumCallback,
+ &context,
+ NULL
+ );
+
+ return result ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
+}
+
+/* KphpSetHandleGrantedAccessEnumCallback
+ *
+ * The callback for KphEnumProcessHandleTable, used by
+ * KphSetHandleGrantedAccess.
+ */
+BOOLEAN KphpSetHandleGrantedAccessEnumCallback(
+ __inout PHANDLE_TABLE_ENTRY HandleTableEntry,
+ __in HANDLE Handle,
+ __in POBP_SET_HANDLE_GRANTED_ACCESS_DATA Context
+ )
+{
+ if (Handle != Context->Handle)
+ return FALSE;
+
+ HandleTableEntry->GrantedAccess = Context->GrantedAccess;
+
+ return TRUE;
+}
+
+/* ObDereferenceProcessHandleTable
+ *
+ * Allows the process to terminate.
+ */
+VOID ObDereferenceProcessHandleTable(
+ __in PEPROCESS Process
+ )
+{
+ KphReleaseProcessRundownProtection(Process);
+}
+
+/* ObDuplicateObject
+ *
+ * Duplicates a handle from the source process to the target process.
+ * WARNING: This does not actually duplicate a handle. It simply
+ * re-opens an object in another process.
+ */
+NTSTATUS ObDuplicateObject(
+ __in PEPROCESS SourceProcess,
+ __in_opt PEPROCESS TargetProcess,
+ __in HANDLE SourceHandle,
+ __out_opt PHANDLE TargetHandle,
+ __in ACCESS_MASK DesiredAccess,
+ __in ULONG HandleAttributes,
+ __in ULONG Options,
+ __in KPROCESSOR_MODE AccessMode
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ BOOLEAN sourceAttached = FALSE;
+ BOOLEAN targetAttached = FALSE;
+ KAPC_STATE apcState;
+ PVOID object;
+ HANDLE objectHandle;
+
+ /* Validate the parameters */
+ if (!TargetProcess || !TargetHandle)
+ {
+ if (!(Options & DUPLICATE_CLOSE_SOURCE))
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ /* Check if we need to attach to the source process */
+ if (SourceProcess != PsGetCurrentProcess())
+ {
+ KeStackAttachProcess(SourceProcess, &apcState);
+ sourceAttached = TRUE;
+ }
+
+ /* If the caller wants us to close the source handle, do it now */
+ if (Options & DUPLICATE_CLOSE_SOURCE)
+ {
+ status = NtClose(SourceHandle);
+ if (sourceAttached)
+ KeUnstackDetachProcess(&apcState);
+
+ return status;
+ }
+
+ /* Reference the object and detach from the source process */
+ status = ObReferenceObjectByHandle(
+ SourceHandle,
+ 0,
+ NULL,
+ KernelMode,
+ &object,
+ NULL
+ );
+ if (sourceAttached)
+ KeUnstackDetachProcess(&apcState);
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ /* Check if we need to attach to the target process */
+ if (TargetProcess != PsGetCurrentProcess())
+ {
+ KeStackAttachProcess(TargetProcess, &apcState);
+ targetAttached = TRUE;
+ }
+
+ /* Open the object and detach from the target process */
+ {
+ POBJECT_TYPE objectType = KphGetObjectTypeNt(object);
+ ACCESS_STATE accessState;
+ CHAR auxData[AUX_ACCESS_DATA_SIZE];
+
+ if (!objectType && AccessMode != KernelMode)
+ {
+ status = STATUS_INVALID_HANDLE;
+ goto OpenObjectEnd;
+ }
+
+ status = SeCreateAccessState(
+ &accessState,
+ (PAUX_ACCESS_DATA)auxData,
+ DesiredAccess,
+ (PGENERIC_MAPPING)KVOFF(objectType, OffOtiGenericMapping)
+ );
+
+ if (!NT_SUCCESS(status))
+ goto OpenObjectEnd;
+
+ accessState.PreviouslyGrantedAccess |= 0xffffffff; /* HACK, doesn't work properly */
+ accessState.RemainingDesiredAccess = 0;
+
+ status = ObOpenObjectByPointer(
+ object,
+ HandleAttributes,
+ &accessState,
+ DesiredAccess,
+ objectType,
+ KernelMode,
+ &objectHandle
+ );
+ SeDeleteAccessState(&accessState);
+ }
+
+OpenObjectEnd:
+ ObDereferenceObject(object);
+
+ if (targetAttached)
+ KeUnstackDetachProcess(&apcState);
+
+ if (NT_SUCCESS(status))
+ *TargetHandle = objectHandle;
+ else
+ *TargetHandle = NULL;
+
+ return status;
+}
+
+/* ObReferenceProcessHandleTable
+ *
+ * Prevents the process from terminating and returns a pointer
+ * to its handle table.
+ */
+PHANDLE_TABLE ObReferenceProcessHandleTable(
+ __in PEPROCESS Process
+ )
+{
+ PHANDLE_TABLE handleTable = NULL;
+
+ if (KphAcquireProcessRundownProtection(Process))
+ {
+ handleTable = *(PHANDLE_TABLE *)KVOFF(Process, OffEpObjectTable);
+
+ if (!handleTable)
+ KphReleaseProcessRundownProtection(Process);
+ }
+
+ return handleTable;
+}
diff --git a/branches/ph-plugins/KProcessHacker/protect.c b/branches/ph-plugins/KProcessHacker/protect.c
new file mode 100644
index 000000000..406c32019
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/protect.c
@@ -0,0 +1,457 @@
+/*
+ * Process Hacker Driver -
+ * process protection
+ *
+ * Copyright (C) 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 .
+ */
+
+#include "include/protect.h"
+
+BOOLEAN KphpIsAccessAllowed(
+ __in PVOID Object,
+ __in KPROCESSOR_MODE AccessMode,
+ __in ACCESS_MASK DesiredAccess
+ );
+
+BOOLEAN KphpIsCurrentProcessProtected();
+
+VOID KphpProtectRemoveEntry(
+ __in PKPH_PROCESS_ENTRY Entry
+ );
+
+/* ProtectedProcessRundownProtect
+ *
+ * Rundown protection making sure this module doesn't deinitialize before all hook targets
+ * have finished executing and no one is accessing the lookaside list.
+ */
+static EX_RUNDOWN_REF ProtectedProcessRundownProtect;
+/* ProtectedProcessListHead
+ *
+ * The head of the process protection linked list. Each entry stores protection
+ * information for a process.
+ */
+static LIST_ENTRY ProtectedProcessListHead;
+/* ProtectedProcessListLock
+ *
+ * The spinlock which protects all accesses to the protected process list (even
+ * the individual entries)
+ */
+static KSPIN_LOCK ProtectedProcessListLock;
+/* ProtectedProcessLookasideList
+ *
+ * The lookaside list for protected process entries.
+ */
+static NPAGED_LOOKASIDE_LIST ProtectedProcessLookasideList;
+
+static KPH_OB_OPEN_HOOK ProcessOpenHook = { 0 };
+static KPH_OB_OPEN_HOOK ThreadOpenHook = { 0 };
+
+/* KphProtectInit
+ *
+ * Initializes process protection.
+ *
+ * IRQL: <= APC_LEVEL
+ */
+NTSTATUS KphProtectInit()
+{
+ NTSTATUS status;
+
+ /* Initialize rundown protection. */
+ ExInitializeRundownProtection(&ProtectedProcessRundownProtect);
+ /* Initialize list structures. */
+ InitializeListHead(&ProtectedProcessListHead);
+ KeInitializeSpinLock(&ProtectedProcessListLock);
+ ExInitializeNPagedLookasideList(
+ &ProtectedProcessLookasideList,
+ NULL,
+ NULL,
+ 0,
+ sizeof(KPH_PROCESS_ENTRY),
+ TAG_PROTECTION_ENTRY,
+ 0
+ );
+
+ /* Hook various functions. */
+ /* Hooking the open procedure calls for processes and threads allows
+ * us to intercept handle creation/duplication/inheritance. */
+ KphInitializeObOpenHook(&ProcessOpenHook, *PsProcessType, KphNewOpenProcedure51, KphNewOpenProcedure60);
+ if (!NT_SUCCESS(status = KphObOpenHook(&ProcessOpenHook)))
+ return status;
+ KphInitializeObOpenHook(&ThreadOpenHook, *PsThreadType, KphNewOpenProcedure51, KphNewOpenProcedure60);
+ if (!NT_SUCCESS(status = KphObOpenHook(&ThreadOpenHook)))
+ return status;
+
+ return STATUS_SUCCESS;
+}
+
+/* KphProtectDeinit
+ *
+ * Removes process protection and frees associated structures.
+ *
+ * IRQL: <= APC_LEVEL
+ */
+NTSTATUS KphProtectDeinit()
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ KIRQL oldIrql;
+ LARGE_INTEGER waitLi;
+
+ /* Unhook. */
+ status = KphObOpenUnhook(&ProcessOpenHook);
+ status = KphObOpenUnhook(&ThreadOpenHook);
+
+ /* Wait for all activity to finish. */
+ ExWaitForRundownProtectionRelease(&ProtectedProcessRundownProtect);
+ /* Wait for a bit (some regions of hook target functions
+ are NOT guarded by rundown protection, e.g.
+ prologues and epilogues). */
+ waitLi.QuadPart = KPH_REL_TIMEOUT_IN_SEC(1);
+ KeDelayExecutionThread(KernelMode, FALSE, &waitLi);
+
+ /* Free all process protection entries. */
+ ExDeleteNPagedLookasideList(&ProtectedProcessLookasideList);
+
+ return status;
+}
+
+/* KphNewOpenProcedure51
+ *
+ * New process/thread open procedure for NT 5.1.
+ */
+NTSTATUS NTAPI KphNewOpenProcedure51(
+ __in OB_OPEN_REASON OpenReason,
+ __in PEPROCESS Process,
+ __in PVOID Object,
+ __in ACCESS_MASK GrantedAccess,
+ __in ULONG HandleCount
+ )
+{
+ /* Simply call the 6.0 open procedure. */
+ /* NOTE: GrantedAccess is always 0 on XP... */
+ return KphNewOpenProcedure60(
+ OpenReason,
+ /* Assume worst case. */
+ UserMode,
+ Process,
+ Object,
+ GrantedAccess,
+ HandleCount
+ );
+}
+
+/* KphNewOpenProcedure60
+ *
+ * New process/thread open procedure for NT 6.0 and 6.1.
+ */
+NTSTATUS NTAPI KphNewOpenProcedure60(
+ __in OB_OPEN_REASON OpenReason,
+ __in KPROCESSOR_MODE AccessMode,
+ __in PEPROCESS Process,
+ __in PVOID Object,
+ __in ACCESS_MASK GrantedAccess,
+ __in ULONG HandleCount
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ BOOLEAN accessAllowed = TRUE;
+
+ /* Prevent the driver from unloading while this routine is executing. */
+ if (!ExAcquireRundownProtection(&ProtectedProcessRundownProtect))
+ {
+ /* Should never happen. */
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ accessAllowed = KphpIsAccessAllowed(
+ Object,
+ AccessMode,
+ /* Assume worst case if granted access not available. */
+ !GrantedAccess ? (ACCESS_MASK)-1 : GrantedAccess
+ );
+
+ if (accessAllowed)
+ {
+ POBJECT_TYPE objectType = KphGetObjectTypeNt(Object);
+
+ /* Call the original open procedure. There shouldn't be any for Windows XP,
+ * while on Windows Vista and 7 it is used for implementing protected
+ * processes (Big Content's DRM protection, not KProcessHacker's protection).
+ */
+ status = KphObOpenCall(
+ objectType == *PsProcessType ? &ProcessOpenHook : &ThreadOpenHook,
+ OpenReason,
+ AccessMode,
+ Process,
+ Object,
+ GrantedAccess,
+ HandleCount
+ );
+ }
+ else
+ {
+ dprintf("KphNewOpenProcedure60: Access denied.\n");
+ status = STATUS_ACCESS_DENIED;
+ }
+
+ ExReleaseRundownProtection(&ProtectedProcessRundownProtect);
+
+ return status;
+}
+
+/* KphProtectAddEntry
+ *
+ * Protects the specified process.
+ *
+ * Thread safety: Full
+ * IRQL: <= DISPATCH_LEVEL
+ */
+PKPH_PROCESS_ENTRY KphProtectAddEntry(
+ __in PEPROCESS Process,
+ __in HANDLE Tag,
+ __in LOGICAL AllowKernelMode,
+ __in ACCESS_MASK ProcessAllowMask,
+ __in ACCESS_MASK ThreadAllowMask
+ )
+{
+ KIRQL oldIrql;
+ PKPH_PROCESS_ENTRY entry;
+
+ /* Prevent the lookaside list from being freed. */
+ if (!ExAcquireRundownProtection(&ProtectedProcessRundownProtect))
+ return NULL;
+
+ entry = ExAllocateFromNPagedLookasideList(&ProtectedProcessLookasideList);
+ /* Lookaside list no longer needed. */
+ ExReleaseRundownProtection(&ProtectedProcessRundownProtect);
+
+ if (!entry)
+ return NULL;
+
+ entry->Process = Process;
+ entry->CreatorProcess = PsGetCurrentProcess();
+ entry->Tag = Tag;
+ entry->AllowKernelMode = AllowKernelMode;
+ entry->ProcessAllowMask = ProcessAllowMask;
+ entry->ThreadAllowMask = ThreadAllowMask;
+
+ KeAcquireSpinLock(&ProtectedProcessListLock, &oldIrql);
+ InsertHeadList(&ProtectedProcessListHead, &entry->ListEntry);
+ KeReleaseSpinLock(&ProtectedProcessListLock, oldIrql);
+
+ return entry;
+}
+
+/* KphProtectFindEntry
+ *
+ * Finds process protection data.
+ *
+ * Thread safety: Full/Limited. The returned pointer is not guaranteed to
+ * point to a valid process entry. However, the copied entry is safe to
+ * read.
+ * IRQL: <= DISPATCH_LEVEL
+ */
+PKPH_PROCESS_ENTRY KphProtectFindEntry(
+ __in PEPROCESS Process,
+ __in HANDLE Tag,
+ __out_opt PKPH_PROCESS_ENTRY ProcessEntryCopy
+ )
+{
+ KIRQL oldIrql;
+ PLIST_ENTRY entry = ProtectedProcessListHead.Flink;
+
+ KeAcquireSpinLock(&ProtectedProcessListLock, &oldIrql);
+
+ while (entry != &ProtectedProcessListHead)
+ {
+ PKPH_PROCESS_ENTRY processEntry =
+ CONTAINING_RECORD(entry, KPH_PROCESS_ENTRY, ListEntry);
+
+ if (
+ (Process != NULL && processEntry->Process == Process) ||
+ (Tag != NULL && processEntry->Tag == Tag)
+ )
+ {
+ /* Copy the entry if requested. */
+ if (ProcessEntryCopy)
+ memcpy(ProcessEntryCopy, processEntry, sizeof(KPH_PROCESS_ENTRY));
+
+ KeReleaseSpinLock(&ProtectedProcessListLock, oldIrql);
+
+ return processEntry;
+ }
+
+ entry = entry->Flink;
+ }
+
+ KeReleaseSpinLock(&ProtectedProcessListLock, oldIrql);
+
+ return NULL;
+}
+
+/* KphProtectRemoveByProcess
+ *
+ * Removes protection from the specified process.
+ *
+ * Thread safety: Limited. Callers must synchronize remove calls such
+ * as KphProtectRemoveByProcess and KphProtectRemoveByTag.
+ * IRQL: <= DISPATCH_LEVEL
+ */
+BOOLEAN KphProtectRemoveByProcess(
+ __in PEPROCESS Process
+ )
+{
+ PKPH_PROCESS_ENTRY entry = KphProtectFindEntry(Process, NULL, NULL);
+
+ if (!entry)
+ return FALSE;
+
+ KphpProtectRemoveEntry(entry);
+
+ return TRUE;
+}
+
+/* KphProtectRemoveByTag
+ *
+ * Removes protection from all processes with the specified tag.
+ *
+ * Thread safety: Limited. Callers must synchronize remove calls such
+ * as KphProtectRemoveByProcess and KphProtectRemoveByTag.
+ * IRQL: <= DISPATCH_LEVEL
+ */
+ULONG KphProtectRemoveByTag(
+ __in HANDLE Tag
+ )
+{
+ KIRQL oldIrql;
+ ULONG count = 0;
+ PKPH_PROCESS_ENTRY entry;
+
+ /* Keep removing entries until we can't find any more. */
+ while (entry = KphProtectFindEntry(NULL, Tag, NULL))
+ {
+ KphpProtectRemoveEntry(entry);
+ count++;
+ }
+
+ return count;
+}
+
+/* KphpIsAccessAllowed
+ *
+ * Checks if the specified access is allowed, according to process
+ * protection rules.
+ *
+ * Thread safety: Full
+ * IRQL: <= DISPATCH_LEVEL
+ */
+BOOLEAN KphpIsAccessAllowed(
+ __in PVOID Object,
+ __in KPROCESSOR_MODE AccessMode,
+ __in ACCESS_MASK DesiredAccess
+ )
+{
+ POBJECT_TYPE objectType;
+ PEPROCESS processObject;
+ BOOLEAN isThread = FALSE;
+
+ objectType = KphGetObjectTypeNt(Object);
+ /* It doesn't matter if it isn't actually a process because we won't be
+ dereferencing it. */
+ processObject = (PEPROCESS)Object;
+ isThread = objectType == *PsThreadType;
+
+ /* If this is a thread, get its parent process. */
+ if (isThread)
+ processObject = IoThreadToProcess((PETHREAD)Object);
+
+ if (
+ processObject != PsGetCurrentProcess() && /* let the caller open its own processes/threads */
+ (objectType == *PsProcessType || objectType == *PsThreadType) /* only protect processes and threads */
+ )
+ {
+ KPH_PROCESS_ENTRY processEntry;
+
+ /* Search for and copy the corresponding process protection entry. */
+ if (KphProtectFindEntry(processObject, NULL, &processEntry))
+ {
+ ACCESS_MASK mask =
+ isThread ? processEntry.ThreadAllowMask : processEntry.ProcessAllowMask;
+
+ /* The process/thread is protected. Check if the requested access is allowed. */
+ if (
+ /* check if kernel-mode is exempt from protection */
+ !(processEntry.AllowKernelMode && AccessMode == KernelMode) &&
+ /* allow the creator of the rule to bypass protection */
+ processEntry.CreatorProcess != PsGetCurrentProcess() &&
+ (DesiredAccess & mask) != DesiredAccess
+ )
+ {
+ /* Access denied. */
+ dprintf(
+ "%d: Access denied: 0x%08x (%s)\n",
+ PsGetCurrentProcessId(),
+ DesiredAccess,
+ isThread ? "Thread" : "Process"
+ );
+
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+/* KphpIsCurrentProcessProtected
+ *
+ * Determines whether the current process is protected.
+ *
+ * Thread safety: Full
+ * IRQL: <= DISPATCH_LEVEL
+ */
+BOOLEAN KphpIsCurrentProcessProtected()
+{
+ return KphProtectFindEntry(PsGetCurrentProcess(), NULL, NULL) != NULL;
+}
+
+/* KphpProtectRemoveEntry
+ *
+ * Removes and frees process protection data.
+ *
+ * Thread safety: Full
+ * IRQL: <= DISPATCH_LEVEL
+ */
+VOID KphpProtectRemoveEntry(
+ __in PKPH_PROCESS_ENTRY Entry
+ )
+{
+ KIRQL oldIrql;
+
+ KeAcquireSpinLock(&ProtectedProcessListLock, &oldIrql);
+ RemoveEntryList(&Entry->ListEntry);
+
+ /* Prevent the lookaside list from being destroyed. */
+ ExAcquireRundownProtection(&ProtectedProcessRundownProtect);
+ ExFreeToNPagedLookasideList(
+ &ProtectedProcessLookasideList,
+ Entry
+ );
+ ExReleaseRundownProtection(&ProtectedProcessRundownProtect);
+
+ KeReleaseSpinLock(&ProtectedProcessListLock, oldIrql);
+}
diff --git a/branches/ph-plugins/KProcessHacker/ps.c b/branches/ph-plugins/KProcessHacker/ps.c
new file mode 100644
index 000000000..ac7502747
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/ps.c
@@ -0,0 +1,1217 @@
+/*
+ * Process Hacker Driver -
+ * processes and threads
+ *
+ * Copyright (C) 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 .
+ */
+
+#include "include/kph.h"
+#include "include/ke.h"
+#include "include/ps.h"
+
+VOID NTAPI KphpCaptureStackBackTraceThreadSpecialApc(
+ PKAPC Apc,
+ PKNORMAL_ROUTINE *NormalRoutine,
+ PVOID *NormalContext,
+ PVOID *SystemArgument1,
+ PVOID *SystemArgument2
+ );
+
+VOID NTAPI KphpExitSpecialApc(
+ PKAPC Apc,
+ PKNORMAL_ROUTINE *NormalRoutine,
+ PVOID *NormalContext,
+ PVOID *SystemArgument1,
+ PVOID *SystemArgument2
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, KphAssignImpersonationToken)
+#pragma alloc_text(PAGE, KphCaptureStackBackTraceThread)
+#pragma alloc_text(PAGE, KphpCaptureStackBackTraceThread)
+#pragma alloc_text(PAGE, KphpCaptureStackBackTraceThreadSpecialApc)
+#pragma alloc_text(PAGE, KphDangerousTerminateThread)
+#pragma alloc_text(PAGE, KphpExitSpecialApc)
+#pragma alloc_text(PAGE, KphGetContextThread)
+#pragma alloc_text(PAGE, KphGetProcessId)
+#pragma alloc_text(PAGE, KphGetThreadId)
+#pragma alloc_text(PAGE, KphGetThreadWin32Thread)
+#pragma alloc_text(PAGE, KphOpenProcess)
+#pragma alloc_text(PAGE, KphOpenProcessJob)
+#pragma alloc_text(PAGE, KphOpenThread)
+#pragma alloc_text(PAGE, KphOpenThreadProcess)
+#pragma alloc_text(PAGE, KphResumeProcess)
+#pragma alloc_text(PAGE, KphSetContextThread)
+#pragma alloc_text(PAGE, KphSuspendProcess)
+#pragma alloc_text(PAGE, KphResumeProcess)
+#pragma alloc_text(PAGE, KphTerminateProcess)
+#pragma alloc_text(PAGE, KphTerminateThread)
+#pragma alloc_text(PAGE, PsTerminateProcess)
+#pragma alloc_text(PAGE, PspTerminateThreadByPointer)
+#endif
+
+/* KphAcquireProcessRundownProtection
+ *
+ * Prevents the process from terminating.
+ */
+BOOLEAN KphAcquireProcessRundownProtection(
+ __in PEPROCESS Process
+ )
+{
+ return ExAcquireRundownProtection((PEX_RUNDOWN_REF)KVOFF(Process, OffEpRundownProtect));
+}
+
+/* KphAssignImpersonationToken
+ *
+ * Assigns an impersonation token to the specified thread.
+ */
+NTSTATUS KphAssignImpersonationToken(
+ __in HANDLE ThreadHandle,
+ __in HANDLE TokenHandle
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PETHREAD threadObject;
+
+ status = ObReferenceObjectByHandle(
+ ThreadHandle,
+ 0,
+ *PsThreadType,
+ KernelMode,
+ &threadObject,
+ NULL
+ );
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ status = PsAssignImpersonationToken(threadObject, TokenHandle);
+ ObDereferenceObject(threadObject);
+
+ return status;
+}
+
+/* KphCaptureStackBackTraceThread
+ *
+ * Captures a kernel-mode stack backtrace for the specified thread.
+ */
+NTSTATUS KphCaptureStackBackTraceThread(
+ __in HANDLE ThreadHandle,
+ __in ULONG FramesToSkip,
+ __in ULONG FramesToCapture,
+ __out_ecount(FramesToCapture) PVOID *BackTrace,
+ __out_opt PULONG CapturedFrames,
+ __out_opt PULONG BackTraceHash,
+ __in KPROCESSOR_MODE AccessMode
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PETHREAD threadObject;
+
+ /* Reference the thread. */
+ status = ObReferenceObjectByHandle(
+ ThreadHandle,
+ THREAD_QUERY_INFORMATION,
+ *PsThreadType,
+ KernelMode,
+ &threadObject,
+ NULL
+ );
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ /* Get the stack trace. */
+ status = KphpCaptureStackBackTraceThread(
+ threadObject,
+ FramesToSkip,
+ FramesToCapture,
+ BackTrace,
+ CapturedFrames,
+ BackTraceHash,
+ AccessMode
+ );
+ /* Dereference the thread. */
+ ObDereferenceObject(threadObject);
+
+ return status;
+}
+
+/* KphpCaptureStackBackTraceThread
+ *
+ * Captures a kernel-mode stack backtrace for the specified thread.
+ *
+ * IRQL: <= APC_LEVEL
+ */
+NTSTATUS KphpCaptureStackBackTraceThread(
+ __in PETHREAD Thread,
+ __in ULONG FramesToSkip,
+ __in ULONG FramesToCapture,
+ __out_ecount(FramesToCapture) PVOID *BackTrace,
+ __out_opt PULONG CapturedFrames,
+ __out_opt PULONG BackTraceHash,
+ __in KPROCESSOR_MODE AccessMode
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ CAPTURE_BACKTRACE_THREAD_CONTEXT context;
+ ULONG backTraceSize;
+ PVOID *backTrace;
+
+ backTraceSize = FramesToCapture * sizeof(PVOID);
+
+ /* Probe user input. */
+ if (AccessMode != KernelMode)
+ {
+ __try
+ {
+ ProbeForWrite(BackTrace, backTraceSize, 1);
+
+ if (CapturedFrames)
+ ProbeForWrite(CapturedFrames, sizeof(ULONG), 1);
+ if (BackTraceHash)
+ ProbeForWrite(BackTraceHash, sizeof(ULONG), 1);
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ return GetExceptionCode();
+ }
+ }
+
+ /* Allocate storage for the stack trace. */
+ backTrace = (PVOID *)ExAllocatePoolWithTag(NonPagedPool, backTraceSize, TAG_CAPTURE_STACK_BACKTRACE);
+
+ if (!backTrace)
+ return STATUS_INSUFFICIENT_RESOURCES;
+
+ /* Initialize the context structure. */
+ context.FramesToSkip = FramesToSkip;
+ context.FramesToCapture = FramesToCapture;
+ context.BackTrace = backTrace;
+
+ /* Check if we're trying to get a stack trace of the current thread. */
+ if (Thread == PsGetCurrentThread())
+ {
+ PCAPTURE_BACKTRACE_THREAD_CONTEXT contextPtr = &context;
+ PVOID dummy = NULL;
+ KIRQL oldIrql;
+
+ context.Local = TRUE;
+ /* Raise the IRQL to APC_LEVEL to simulate an APC environment. */
+ KeRaiseIrql(APC_LEVEL, &oldIrql);
+ /* Call the APC routine directly. */
+ KphpCaptureStackBackTraceThreadSpecialApc(
+ &context.Apc,
+ NULL,
+ NULL,
+ &contextPtr,
+ &dummy
+ );
+ /* Lower the IRQL back. */
+ KeLowerIrql(oldIrql);
+ }
+ else
+ {
+ context.Local = FALSE;
+ /* Initialize the stack trace completed event. */
+ KeInitializeEvent(&context.CompletedEvent, NotificationEvent, FALSE);
+ /* Initialize the APC. */
+ KeInitializeApc(
+ &context.Apc,
+ (PKTHREAD)Thread,
+ OriginalApcEnvironment,
+ KphpCaptureStackBackTraceThreadSpecialApc,
+ NULL,
+ NULL,
+ KernelMode,
+ NULL
+ );
+ /* Queue the APC. */
+ if (KeInsertQueueApc(&context.Apc, &context, NULL, 2))
+ {
+ /* Wait for the APC to complete. */
+ status = KeWaitForSingleObject(
+ &context.CompletedEvent,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL
+ );
+ }
+ else
+ {
+ status = STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ if (NT_SUCCESS(status))
+ {
+ ASSERT(context.CapturedFrames <= FramesToCapture);
+
+ /* Write the information. */
+ __try
+ {
+ memcpy(BackTrace, backTrace, context.CapturedFrames * sizeof(PVOID));
+
+ if (CapturedFrames)
+ *CapturedFrames = context.CapturedFrames;
+ if (BackTraceHash)
+ *BackTraceHash = context.BackTraceHash;
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ status = GetExceptionCode();
+ }
+ }
+
+ /* Free the allocated stack trace storage. */
+ ExFreePoolWithTag(backTrace, TAG_CAPTURE_STACK_BACKTRACE);
+
+ return status;
+}
+
+/* KphpCaptureStackBackTraceThreadSpecialApc
+ *
+ * The special APC routine which captures a thread stack trace.
+ */
+VOID NTAPI KphpCaptureStackBackTraceThreadSpecialApc(
+ PKAPC Apc,
+ PKNORMAL_ROUTINE *NormalRoutine,
+ PVOID *NormalContext,
+ PVOID *SystemArgument1,
+ PVOID *SystemArgument2
+ )
+{
+ PCAPTURE_BACKTRACE_THREAD_CONTEXT context =
+ (PCAPTURE_BACKTRACE_THREAD_CONTEXT)*SystemArgument1;
+
+ /* Capture a stack trace. */
+ context->CapturedFrames = KphCaptureStackBackTrace(
+ context->FramesToSkip,
+ context->FramesToCapture,
+ 0,
+ context->BackTrace,
+ &context->BackTraceHash
+ );
+
+ if (!context->Local)
+ {
+ /* Signal the completed event. */
+ KeSetEvent(&context->CompletedEvent, 0, FALSE);
+ }
+}
+
+/* KphDangerousTerminateThread
+ *
+ * Terminates the specified thread by queueing an APC.
+ */
+NTSTATUS KphDangerousTerminateThread(
+ __in HANDLE ThreadHandle,
+ __in NTSTATUS ExitStatus
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PETHREAD threadObject;
+
+ if (!__PspTerminateThreadByPointer)
+ return STATUS_NOT_SUPPORTED;
+
+ status = ObReferenceObjectByHandle(
+ ThreadHandle,
+ THREAD_TERMINATE,
+ *PsThreadType,
+ KernelMode,
+ &threadObject,
+ NULL
+ );
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ if (threadObject != PsGetCurrentThread())
+ {
+ EXIT_THREAD_CONTEXT context;
+
+ /* Initialize the context structure. */
+ context.ExitStatus = ExitStatus;
+ /* Initialize the completion event. */
+ KeInitializeEvent(&context.CompletedEvent, NotificationEvent, FALSE);
+ /* Initialize the APC. */
+ KeInitializeApc(
+ &context.Apc,
+ (PKTHREAD)threadObject,
+ OriginalApcEnvironment,
+ KphpExitSpecialApc,
+ NULL,
+ NULL,
+ KernelMode,
+ NULL
+ );
+
+ /* Queue the APC. */
+ if (KeInsertQueueApc(&context.Apc, &context, NULL, 2))
+ {
+ /* Wait for the APC to initialize. */
+ status = KeWaitForSingleObject(
+ &context.CompletedEvent,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL
+ );
+ }
+ else
+ {
+ status = STATUS_UNSUCCESSFUL;
+ }
+
+ ObDereferenceObject(threadObject);
+ }
+ else
+ {
+ /* Can't terminate self. */
+ ObDereferenceObject(threadObject);
+ return STATUS_CANT_TERMINATE_SELF;
+ }
+
+ return status;
+}
+
+VOID NTAPI KphpExitSpecialApc(
+ PKAPC Apc,
+ PKNORMAL_ROUTINE *NormalRoutine,
+ PVOID *NormalContext,
+ PVOID *SystemArgument1,
+ PVOID *SystemArgument2
+ )
+{
+ PEXIT_THREAD_CONTEXT context =
+ (PEXIT_THREAD_CONTEXT)*SystemArgument1;
+ NTSTATUS exitStatus;
+
+ /* Get the exit status. */
+ exitStatus = context->ExitStatus;
+ /* That's the best we can do. Once we exit the current thread we can't
+ * signal the event, so just signal it now. */
+ KeSetEvent(&context->CompletedEvent, 0, FALSE);
+ /* Exit the thread by calling PspTerminateThreadByPointer. */
+ PspTerminateThreadByPointer(PsGetCurrentThread(), exitStatus);
+ /* Should never happen. */
+ dfprintf(
+ "WARNING: Thread was not terminated by PspTerminateThreadByPointer: %d, %#x\n",
+ PsGetCurrentThreadId(),
+ PsGetCurrentThread()
+ );
+}
+
+/* KphGetContextThread
+ *
+ * Gets the context of the specified thread.
+ */
+NTSTATUS KphGetContextThread(
+ __in HANDLE ThreadHandle,
+ __inout PCONTEXT ThreadContext,
+ __in KPROCESSOR_MODE AccessMode
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PETHREAD threadObject;
+
+ status = ObReferenceObjectByHandle(
+ ThreadHandle,
+ THREAD_GET_CONTEXT,
+ *PsThreadType,
+ KernelMode,
+ &threadObject,
+ NULL
+ );
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ status = PsGetContextThread(threadObject, ThreadContext, AccessMode);
+ ObDereferenceObject(threadObject);
+
+ return status;
+}
+
+/* KphGetProcessId
+ *
+ * Gets the ID of the process referenced by the specified handle.
+ */
+HANDLE KphGetProcessId(
+ __in HANDLE ProcessHandle
+ )
+{
+ PEPROCESS processObject;
+ HANDLE processId;
+
+ if (!NT_SUCCESS(ObReferenceObjectByHandle(ProcessHandle, 0,
+ *PsProcessType, KernelMode, &processObject, NULL)))
+ return 0;
+
+ processId = PsGetProcessId(processObject);
+ ObDereferenceObject(processObject);
+
+ return processId;
+}
+
+/* KphGetThreadId
+ *
+ * Gets the ID of the thread referenced by the specified handle,
+ * and optionally the ID of the thread's process.
+ */
+HANDLE KphGetThreadId(
+ __in HANDLE ThreadHandle,
+ __out_opt PHANDLE ProcessId
+ )
+{
+ PETHREAD threadObject;
+ CLIENT_ID clientId;
+
+ if (!NT_SUCCESS(ObReferenceObjectByHandle(ThreadHandle, 0,
+ *PsThreadType, KernelMode, &threadObject, NULL)))
+ return 0;
+
+ clientId = *(PCLIENT_ID)KVOFF(threadObject, OffEtClientId);
+
+ ObDereferenceObject(threadObject);
+
+ if (ProcessId)
+ {
+ *ProcessId = clientId.UniqueProcess;
+ }
+
+ return clientId.UniqueThread;
+}
+
+/* KphGetThreadWin32Thread
+ *
+ * Gets a pointer to the WIN32THREAD structure of the specified thread.
+ */
+NTSTATUS KphGetThreadWin32Thread(
+ __in HANDLE ThreadHandle,
+ __out PVOID *Win32Thread,
+ __in KPROCESSOR_MODE AccessMode
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PETHREAD threadObject;
+ PVOID win32Thread;
+
+ if (AccessMode != KernelMode)
+ {
+ __try
+ {
+ ProbeForWrite(Win32Thread, sizeof(PVOID), 1);
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ return GetExceptionCode();
+ }
+ }
+
+ status = ObReferenceObjectByHandle(
+ ThreadHandle,
+ 0,
+ *PsThreadType,
+ KernelMode,
+ &threadObject,
+ NULL
+ );
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ win32Thread = PsGetThreadWin32Thread(threadObject);
+ ObDereferenceObject(threadObject);
+
+ __try
+ {
+ *Win32Thread = win32Thread;
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ return GetExceptionCode();
+ }
+
+ return status;
+}
+
+/* KphOpenProcess
+ *
+ * Opens a process.
+ */
+NTSTATUS KphOpenProcess(
+ __out PHANDLE ProcessHandle,
+ __in ACCESS_MASK DesiredAccess,
+ __in POBJECT_ATTRIBUTES ObjectAttributes,
+ __in_opt PCLIENT_ID ClientId,
+ __in KPROCESSOR_MODE AccessMode
+ )
+{
+ BOOLEAN hasObjectName = ObjectAttributes->ObjectName != NULL;
+ ULONG attributes = ObjectAttributes->Attributes;
+ NTSTATUS status = STATUS_SUCCESS;
+ ACCESS_STATE accessState;
+ CHAR auxData[AUX_ACCESS_DATA_SIZE];
+ PEPROCESS processObject = NULL;
+ PETHREAD threadObject = NULL;
+ HANDLE processHandle = NULL;
+
+ if (hasObjectName && ClientId)
+ return STATUS_INVALID_PARAMETER_MIX;
+
+ /* ReactOS code cleared this bit up for me :) */
+ status = SeCreateAccessState(
+ &accessState,
+ (PAUX_ACCESS_DATA)auxData,
+ DesiredAccess,
+ (PGENERIC_MAPPING)KVOFF(*PsProcessType, OffOtiGenericMapping)
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ return status;
+ }
+
+ /* Let's hope our client isn't a virus... */
+ if (accessState.RemainingDesiredAccess & MAXIMUM_ALLOWED)
+ accessState.PreviouslyGrantedAccess |= ProcessAllAccess;
+ else
+ accessState.PreviouslyGrantedAccess |= accessState.RemainingDesiredAccess;
+
+ accessState.RemainingDesiredAccess = 0;
+
+ if (hasObjectName)
+ {
+ status = ObOpenObjectByName(
+ ObjectAttributes,
+ *PsProcessType,
+ AccessMode,
+ &accessState,
+ 0,
+ NULL,
+ &processHandle
+ );
+ SeDeleteAccessState(&accessState);
+ }
+ else if (ClientId)
+ {
+ if (ClientId->UniqueThread)
+ {
+ status = PsLookupProcessThreadByCid(ClientId, &processObject, &threadObject);
+ }
+ else
+ {
+ status = PsLookupProcessByProcessId(ClientId->UniqueProcess, &processObject);
+ }
+
+ if (!NT_SUCCESS(status))
+ {
+ SeDeleteAccessState(&accessState);
+ return status;
+ }
+
+ status = ObOpenObjectByPointer(
+ processObject,
+ attributes,
+ &accessState,
+ 0,
+ *PsProcessType,
+ AccessMode,
+ &processHandle
+ );
+
+ SeDeleteAccessState(&accessState);
+ ObDereferenceObject(processObject);
+
+ if (threadObject)
+ ObDereferenceObject(threadObject);
+ }
+ else
+ {
+ SeDeleteAccessState(&accessState);
+ return STATUS_INVALID_PARAMETER_MIX;
+ }
+
+ if (NT_SUCCESS(status))
+ {
+ *ProcessHandle = processHandle;
+ }
+
+ return status;
+}
+
+/* KphOpenProcessJob
+ *
+ * Opens the specified process' job object. If the process has
+ * not been assigned to a job object, the function returns
+ * STATUS_PROCESS_NOT_IN_JOB.
+ */
+NTSTATUS KphOpenProcessJob(
+ __in HANDLE ProcessHandle,
+ __in ACCESS_MASK DesiredAccess,
+ __out PHANDLE JobHandle,
+ __in KPROCESSOR_MODE AccessMode
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PEPROCESS processObject;
+ PVOID jobObject;
+ HANDLE jobHandle;
+ ACCESS_STATE accessState;
+ CHAR auxData[AUX_ACCESS_DATA_SIZE];
+
+ status = SeCreateAccessState(
+ &accessState,
+ (PAUX_ACCESS_DATA)auxData,
+ DesiredAccess,
+ (PGENERIC_MAPPING)KVOFF(*PsJobType, OffOtiGenericMapping)
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ return status;
+ }
+
+ if (accessState.RemainingDesiredAccess & MAXIMUM_ALLOWED)
+ accessState.PreviouslyGrantedAccess |= JOB_OBJECT_ALL_ACCESS;
+ else
+ accessState.PreviouslyGrantedAccess |= accessState.RemainingDesiredAccess;
+
+ accessState.RemainingDesiredAccess = 0;
+
+ status = ObReferenceObjectByHandle(ProcessHandle, 0, *PsProcessType, KernelMode, &processObject, 0);
+
+ if (!NT_SUCCESS(status))
+ {
+ SeDeleteAccessState(&accessState);
+ return status;
+ }
+
+ /* If we have PsGetProcessJob, use it. Otherwise, read the EPROCESS structure. */
+ if (PsGetProcessJob)
+ {
+ jobObject = PsGetProcessJob(processObject);
+ }
+ else
+ {
+ jobObject = *(PVOID *)((PCHAR)processObject + OffEpJob);
+ }
+
+ ObDereferenceObject(processObject);
+
+ if (jobObject == NULL)
+ {
+ /* No such job. Output a NULL handle and exit. */
+ SeDeleteAccessState(&accessState);
+ *JobHandle = NULL;
+ return STATUS_PROCESS_NOT_IN_JOB;
+ }
+
+ ObReferenceObject(jobObject);
+ status = ObOpenObjectByPointer(
+ jobObject,
+ 0,
+ &accessState,
+ 0,
+ *PsJobType,
+ AccessMode,
+ &jobHandle
+ );
+ SeDeleteAccessState(&accessState);
+ ObDereferenceObject(jobObject);
+
+ if (NT_SUCCESS(status))
+ *JobHandle = jobHandle;
+
+ return status;
+}
+
+/* KphOpenThread
+ *
+ * Opens a thread.
+ */
+NTSTATUS KphOpenThread(
+ __out PHANDLE ThreadHandle,
+ __in ACCESS_MASK DesiredAccess,
+ __in POBJECT_ATTRIBUTES ObjectAttributes,
+ __in_opt PCLIENT_ID ClientId,
+ __in KPROCESSOR_MODE AccessMode
+ )
+{
+ BOOLEAN hasObjectName = ObjectAttributes->ObjectName != NULL;
+ ULONG attributes = ObjectAttributes->Attributes;
+ NTSTATUS status = STATUS_SUCCESS;
+ ACCESS_STATE accessState;
+ CHAR auxData[AUX_ACCESS_DATA_SIZE];
+ PETHREAD threadObject = NULL;
+ HANDLE threadHandle = NULL;
+
+ if (hasObjectName && ClientId)
+ return STATUS_INVALID_PARAMETER_MIX;
+
+ status = SeCreateAccessState(
+ &accessState,
+ (PAUX_ACCESS_DATA)auxData,
+ DesiredAccess,
+ (PGENERIC_MAPPING)KVOFF(*PsThreadType, OffOtiGenericMapping)
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ return status;
+ }
+
+ if (accessState.RemainingDesiredAccess & MAXIMUM_ALLOWED)
+ accessState.PreviouslyGrantedAccess |= ThreadAllAccess;
+ else
+ accessState.PreviouslyGrantedAccess |= accessState.RemainingDesiredAccess;
+
+ accessState.RemainingDesiredAccess = 0;
+
+ if (hasObjectName)
+ {
+ status = ObOpenObjectByName(
+ ObjectAttributes,
+ *PsThreadType,
+ AccessMode,
+ &accessState,
+ 0,
+ NULL,
+ &threadHandle
+ );
+ SeDeleteAccessState(&accessState);
+ }
+ else if (ClientId)
+ {
+ if (ClientId->UniqueProcess)
+ {
+ status = PsLookupProcessThreadByCid(ClientId, NULL, &threadObject);
+ }
+ else
+ {
+ status = PsLookupThreadByThreadId(ClientId->UniqueThread, &threadObject);
+ }
+
+ if (!NT_SUCCESS(status))
+ {
+ SeDeleteAccessState(&accessState);
+ return status;
+ }
+
+ status = ObOpenObjectByPointer(
+ threadObject,
+ attributes,
+ &accessState,
+ 0,
+ *PsThreadType,
+ AccessMode,
+ &threadHandle
+ );
+
+ SeDeleteAccessState(&accessState);
+ ObDereferenceObject(threadObject);
+ }
+ else
+ {
+ SeDeleteAccessState(&accessState);
+ return STATUS_INVALID_PARAMETER_MIX;
+ }
+
+ if (NT_SUCCESS(status))
+ {
+ *ThreadHandle = threadHandle;
+ }
+
+ return status;
+}
+
+/* KphOpenThreadProcess
+ *
+ * Opens a thread's process.
+ */
+NTSTATUS KphOpenThreadProcess(
+ __in HANDLE ThreadHandle,
+ __in ACCESS_MASK DesiredAccess,
+ __out PHANDLE ProcessHandle,
+ __in KPROCESSOR_MODE AccessMode
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PETHREAD threadObject;
+ PEPROCESS processObject;
+ HANDLE processHandle;
+ ACCESS_STATE accessState;
+ CHAR auxData[AUX_ACCESS_DATA_SIZE];
+
+ status = SeCreateAccessState(
+ &accessState,
+ (PAUX_ACCESS_DATA)auxData,
+ DesiredAccess,
+ (PGENERIC_MAPPING)KVOFF(*PsProcessType, OffOtiGenericMapping)
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ return status;
+ }
+
+ if (accessState.RemainingDesiredAccess & MAXIMUM_ALLOWED)
+ accessState.PreviouslyGrantedAccess |= ProcessAllAccess;
+ else
+ accessState.PreviouslyGrantedAccess |= accessState.RemainingDesiredAccess;
+
+ accessState.RemainingDesiredAccess = 0;
+
+ status = ObReferenceObjectByHandle(ThreadHandle, 0, *PsThreadType, KernelMode, &threadObject, 0);
+
+ if (!NT_SUCCESS(status))
+ {
+ SeDeleteAccessState(&accessState);
+ return status;
+ }
+
+ /* Get the process object. */
+ processObject = IoThreadToProcess(threadObject);
+ ObDereferenceObject(threadObject);
+
+ if (processObject == NULL)
+ {
+ /* Thread does not have a process (?). */
+ SeDeleteAccessState(&accessState);
+ *ProcessHandle = NULL;
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ ObReferenceObject(processObject);
+ status = ObOpenObjectByPointer(
+ processObject,
+ 0,
+ &accessState,
+ 0,
+ *PsProcessType,
+ AccessMode,
+ &processHandle
+ );
+ SeDeleteAccessState(&accessState);
+ ObDereferenceObject(processObject);
+
+ if (NT_SUCCESS(status))
+ *ProcessHandle = processHandle;
+
+ return status;
+}
+
+/* KphReleaseProcessRundownProtection
+ *
+ * Allows the process to terminate.
+ */
+VOID KphReleaseProcessRundownProtection(
+ __in PEPROCESS Process
+ )
+{
+ ExReleaseRundownProtection((PEX_RUNDOWN_REF)KVOFF(Process, OffEpRundownProtect));
+}
+
+/* KphResumeProcess
+ *
+ * Resumes the specified process.
+ */
+NTSTATUS KphResumeProcess(
+ __in HANDLE ProcessHandle
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PEPROCESS processObject;
+
+ if (!PsResumeProcess)
+ return STATUS_NOT_SUPPORTED;
+
+ status = ObReferenceObjectByHandle(
+ ProcessHandle,
+ PROCESS_SUSPEND_RESUME,
+ *PsProcessType,
+ KernelMode,
+ &processObject,
+ NULL);
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ status = PsResumeProcess(processObject);
+ ObDereferenceObject(processObject);
+
+ return status;
+}
+
+/* KphSetContextThread
+ *
+ * Sets the context of the specified thread.
+ */
+NTSTATUS KphSetContextThread(
+ __in HANDLE ThreadHandle,
+ __in PCONTEXT ThreadContext,
+ __in KPROCESSOR_MODE AccessMode
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PETHREAD threadObject;
+
+ status = ObReferenceObjectByHandle(
+ ThreadHandle,
+ THREAD_SET_CONTEXT,
+ *PsThreadType,
+ KernelMode,
+ &threadObject,
+ NULL);
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ status = PsSetContextThread(threadObject, ThreadContext, AccessMode);
+ ObDereferenceObject(threadObject);
+
+ return status;
+}
+
+/* KphSuspendProcess
+ *
+ * Suspends the specified process.
+ */
+NTSTATUS KphSuspendProcess(
+ __in HANDLE ProcessHandle
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PEPROCESS processObject;
+
+ if (!PsSuspendProcess)
+ return STATUS_NOT_SUPPORTED;
+
+ status = ObReferenceObjectByHandle(
+ ProcessHandle,
+ PROCESS_SUSPEND_RESUME,
+ *PsProcessType,
+ KernelMode,
+ &processObject,
+ NULL);
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ status = PsSuspendProcess(processObject);
+ ObDereferenceObject(processObject);
+
+ return status;
+}
+
+/* KphTerminateProcess
+ *
+ * Terminates the specified process.
+ */
+NTSTATUS KphTerminateProcess(
+ __in HANDLE ProcessHandle,
+ __in NTSTATUS ExitStatus
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PEPROCESS processObject;
+
+ status = ObReferenceObjectByHandle(
+ ProcessHandle,
+ PROCESS_TERMINATE,
+ *PsProcessType,
+ KernelMode,
+ &processObject,
+ NULL);
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ /* Can't terminate ourself. Get user-mode to do it. */
+ if (processObject == PsGetCurrentProcess())
+ {
+ ObDereferenceObject(processObject);
+ return STATUS_CANT_TERMINATE_SELF;
+ }
+
+ /* If we have located PsTerminateProcess/PspTerminateProcess,
+ call it. */
+ if (__PsTerminateProcess)
+ {
+ status = PsTerminateProcess(processObject, ExitStatus);
+ ObDereferenceObject(processObject);
+ }
+ else
+ {
+ /* Otherwise, we'll have to call ZwTerminateProcess - most hooks on this function
+ allow kernel-mode callers through. */
+ OBJECT_ATTRIBUTES objectAttributes = { 0 };
+ CLIENT_ID clientId;
+ HANDLE newProcessHandle;
+
+ /* We have to open it again because ZwTerminateProcess only accepts kernel handles. */
+ clientId.UniqueThread = 0;
+ clientId.UniqueProcess = PsGetProcessId(processObject);
+ status = KphOpenProcess(&newProcessHandle, 0x1, &objectAttributes, &clientId, KernelMode);
+ ObDereferenceObject(processObject);
+
+ if (NT_SUCCESS(status))
+ {
+ status = ZwTerminateProcess(newProcessHandle, ExitStatus);
+ ZwClose(newProcessHandle);
+ }
+ }
+
+ return status;
+}
+
+/* KphTerminateThread
+ *
+ * Terminates the specified thread.
+ */
+NTSTATUS KphTerminateThread(
+ __in HANDLE ThreadHandle,
+ __in NTSTATUS ExitStatus
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PETHREAD threadObject;
+
+ status = ObReferenceObjectByHandle(
+ ThreadHandle,
+ THREAD_TERMINATE,
+ *PsThreadType,
+ KernelMode,
+ &threadObject,
+ NULL);
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ if (threadObject != PsGetCurrentThread())
+ {
+ status = PspTerminateThreadByPointer(threadObject, ExitStatus);
+ ObDereferenceObject(threadObject);
+ }
+ else
+ {/*
+ ObDereferenceObject(threadObject);
+ status = PspTerminateThreadByPointer(PsGetCurrentThread(), ExitStatus); */
+ /* Leads to bugs, so don't terminate self. */
+ ObDereferenceObject(threadObject);
+ return STATUS_CANT_TERMINATE_SELF;
+ }
+
+ return status;
+}
+
+/* PsTerminateProcess
+ *
+ * Terminates the specified process. If PsTerminateProcess or
+ * PspTerminateProcess could not be located, the call will fail
+ * with STATUS_NOT_SUPPORTED.
+ */
+NTSTATUS PsTerminateProcess(
+ __in PEPROCESS Process,
+ __in NTSTATUS ExitStatus
+ )
+{
+ PVOID psTerminateProcess = __PsTerminateProcess;
+ NTSTATUS status;
+
+ if (!psTerminateProcess)
+ return STATUS_NOT_SUPPORTED;
+
+#ifdef _X86_
+ if (WindowsVersion == WINDOWS_XP)
+ {
+ /* PspTerminateProcess on XP is stdcall. */
+ __asm
+ {
+ push [ExitStatus]
+ push [Process]
+ call [psTerminateProcess]
+ mov [status], eax
+ }
+ }
+ else if (
+ WindowsVersion == WINDOWS_VISTA ||
+ WindowsVersion == WINDOWS_7
+ )
+ {
+ /* PsTerminateProcess on Vista and above is thiscall. */
+ __asm
+ {
+ push [ExitStatus]
+ mov ecx, [Process]
+ call [psTerminateProcess]
+ mov [status], eax
+ }
+ }
+ else
+ {
+ return STATUS_NOT_SUPPORTED;
+ }
+#else
+ status = __PsTerminateProcess(Process, ExitStatus);
+#endif
+
+ return status;
+}
+
+/* PspTerminateThreadByPointer
+ *
+ * Terminates the specified thread. If PspTerminateThreadByPointer
+ * could not be located, the call will fail with STATUS_NOT_SUPPORTED.
+ */
+NTSTATUS PspTerminateThreadByPointer(
+ __in PETHREAD Thread,
+ __in NTSTATUS ExitStatus
+ )
+{
+ PVOID pspTerminateThreadByPointer = __PspTerminateThreadByPointer;
+
+ if (!pspTerminateThreadByPointer)
+ return STATUS_NOT_SUPPORTED;
+
+ if (WindowsVersion == WINDOWS_XP)
+ {
+ return ((_PspTerminateThreadByPointer51)pspTerminateThreadByPointer)(
+ Thread,
+ ExitStatus
+ );
+ }
+ else if (
+ WindowsVersion == WINDOWS_VISTA ||
+ WindowsVersion == WINDOWS_7
+ )
+ {
+ return ((_PspTerminateThreadByPointer60)pspTerminateThreadByPointer)(
+ Thread,
+ ExitStatus,
+ Thread == PsGetCurrentThread()
+ );
+ }
+ else
+ {
+ return STATUS_NOT_SUPPORTED;
+ }
+}
diff --git a/branches/ph-plugins/KProcessHacker/ref.c b/branches/ph-plugins/KProcessHacker/ref.c
new file mode 100644
index 000000000..eeb0f917a
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/ref.c
@@ -0,0 +1,574 @@
+/*
+ * Process Hacker Driver -
+ * internal object manager
+ *
+ * Copyright (C) 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 .
+ */
+
+#include "include/refp.h"
+
+/* A list of all objects created by the object manager. */
+LIST_ENTRY KphObjectListHead;
+/* A mutex protecting global data structures. */
+FAST_MUTEX KphObjectListMutex;
+/* The object type type. */
+PKPH_OBJECT_TYPE KphObjectTypeObject = NULL;
+
+/* Whether the object manager is destroying all objects. */
+BOOLEAN KphObjectDeinitializing = FALSE;
+/* The work item for deferred object deletes. */
+WORK_QUEUE_ITEM KphObjectDeferDeleteWorkItem;
+/* The next object to delete. */
+PKPH_OBJECT_HEADER KphObjectNextToFree = NULL;
+
+/* KphRefInit
+ *
+ * Initializes the KPH object manager.
+ *
+ * IRQL: <= APC_LEVEL
+ */
+NTSTATUS KphRefInit()
+{
+ NTSTATUS status = STATUS_SUCCESS;
+
+ /* Initialize the object list. */
+ InitializeListHead(&KphObjectListHead);
+ /* Initialize the object list mutex. */
+ ExInitializeFastMutex(&KphObjectListMutex);
+
+ /* Initialize the deferred delete work item. */
+ ExInitializeWorkItem(
+ &KphObjectDeferDeleteWorkItem,
+ KphpDeferDeleteObjectRoutine,
+ NULL
+ );
+
+ /* Create the fundamental object type. */
+ status = KphCreateObjectType(
+ &KphObjectTypeObject,
+ NonPagedPool,
+ 0,
+ NULL
+ );
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ /* Now that the fundamental object type exists, fix it up. */
+ KphObjectToObjectHeader(KphObjectTypeObject)->Type = KphObjectTypeObject;
+ KphObjectTypeObject->NumberOfObjects = 1;
+
+ return status;
+}
+
+/* KphRefDeinit
+ *
+ * Frees all objects created by the KPH object manager.
+ *
+ * IRQL: = PASSIVE_LEVEL
+ */
+NTSTATUS KphRefDeinit()
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PLIST_ENTRY currentEntry;
+
+ KphObjectDeinitializing = TRUE;
+
+ /* Acquire the object list mutex to make sure no one else
+ * modifies the list. */
+ ExAcquireFastMutex(&KphObjectListMutex);
+
+ /* Remove and free all objects in the list. */
+ while ((currentEntry = RemoveHeadList(&KphObjectListHead)) != &KphObjectListHead)
+ {
+ PKPH_OBJECT_HEADER objectHeader =
+ CONTAINING_RECORD(currentEntry, KPH_OBJECT_HEADER, GlobalObjectListEntry);
+
+ /* Free the object, ignoring its reference count. */
+ KphpFreeObject(objectHeader);
+ }
+
+ /* Release the object list mutex and restore the IRQL. */
+ ExReleaseFastMutex(&KphObjectListMutex);
+
+ return STATUS_SUCCESS;
+}
+
+/* KphCreateObject
+ *
+ * Allocates a object.
+ *
+ * Object: A variable which receives a pointer to the newly allocated object.
+ * ObjectSize: The size of the object.
+ * Flags: A combination of flags specifying how the object is to be allocated.
+ * * KPHOBJ_RAISE_ON_FAIL: An exception will be raised if the object could
+ * not be allocated.
+ * * KPHOBJ_PAGED_POOL: The object will be allocated in the paged pool. If
+ * this flag is specified, KPHOBJ_NONPAGED_POOL cannot be specified.
+ * * KPHOBJ_NONPAGED_POOL: The object will be allocated in the non-paged pool.
+ * If this flag is specified, KPHOBJ_PAGED_POOL cannot be specified.
+ * ObjectType: The type of the object.
+ * AdditionalReferences: The number of references to add to the object. The
+ * object will have a reference count of 1 + AdditionalReferences.
+ *
+ * IRQL: <= APC_LEVEL
+ */
+NTSTATUS KphCreateObject(
+ __out PVOID *Object,
+ __in SIZE_T ObjectSize,
+ __in ULONG Flags,
+ __in_opt PKPH_OBJECT_TYPE ObjectType,
+ __in_opt LONG AdditionalReferences
+ )
+{
+ PKPH_OBJECT_HEADER objectHeader;
+ POOL_TYPE poolType;
+
+ /* Check the flags. */
+ if ((Flags & KPHOBJ_VALID_FLAGS) != Flags) /* Valid flag mask */
+ return STATUS_INVALID_PARAMETER_3;
+ if ((Flags & KPHOBJ_PAGED_POOL) && (Flags & KPHOBJ_NONPAGED_POOL)) /* Can't be both pools */
+ return STATUS_INVALID_PARAMETER_3;
+ /* The object type is only optional if the fundamental object type
+ * hasn't been created. */
+ if (!ObjectType && KphObjectTypeObject)
+ return STATUS_INVALID_PARAMETER_4;
+ /* Make sure the additional reference count isn't negative. */
+ if (AdditionalReferences < 0)
+ return STATUS_INVALID_PARAMETER_5;
+
+ /* Figure out the pool type. If it wasn't specified in Flags,
+ * get the pool type from the object type. */
+ if (Flags & KPHOBJ_PAGED_POOL)
+ poolType = PagedPool;
+ else if (Flags & KPHOBJ_NONPAGED_POOL)
+ poolType = NonPagedPool;
+ else if (ObjectType) /* May be null if we're creating the fundamental type */
+ poolType = ObjectType->DefaultPoolType;
+ else
+ poolType = NonPagedPool;
+
+ /* Allocate storage for the object. Note that this includes
+ * the object header followed by the object body. */
+ objectHeader = KphpAllocateObject(ObjectSize, poolType);
+
+ if (!objectHeader)
+ {
+ if (Flags & KPHOBJ_RAISE_ON_FAIL)
+ ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
+ else
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ /* Object type statistics. */
+ if (ObjectType)
+ {
+ InterlockedIncrement(&ObjectType->NumberOfObjects);
+ }
+
+ /* Initialize the object header. */
+ objectHeader->RefCount = 1 + AdditionalReferences;
+ objectHeader->Flags = Flags;
+ objectHeader->Size = ObjectSize;
+ objectHeader->Type = ObjectType;
+
+ /* Insert the object into the global object list. */
+ ExAcquireFastMutex(&KphObjectListMutex);
+ InsertHeadList(&KphObjectListHead, &objectHeader->GlobalObjectListEntry);
+ ExReleaseFastMutex(&KphObjectListMutex);
+
+ /* Pass a pointer to the object body back to the caller. */
+ *Object = KphObjectHeaderToObject(objectHeader);
+
+ return STATUS_SUCCESS;
+}
+
+/* KphCreateObjectType
+ *
+ * Creates an object type.
+ *
+ * IRQL: <= APC_LEVEL
+ */
+NTSTATUS KphCreateObjectType(
+ __out PKPH_OBJECT_TYPE *ObjectType,
+ __in POOL_TYPE DefaultPoolType,
+ __in ULONG Flags,
+ __in PKPH_TYPE_DELETE_PROCEDURE DeleteProcedure
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PKPH_OBJECT_TYPE objectType;
+
+ /* Check the flags. */
+ if ((Flags & KPHOBJTYPE_VALID_FLAGS) != Flags) /* Valid flag mask */
+ return STATUS_INVALID_PARAMETER_3;
+
+ /* Create the type object. */
+ status = KphCreateObject(
+ &objectType,
+ sizeof(KPH_OBJECT_TYPE),
+ 0,
+ KphObjectTypeObject,
+ 0
+ );
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ /* Initialize the type object. */
+ objectType->DefaultPoolType = DefaultPoolType;
+ objectType->Flags = Flags;
+ objectType->DeleteProcedure = DeleteProcedure;
+ objectType->NumberOfObjects = 0;
+
+ *ObjectType = objectType;
+
+ return status;
+}
+
+/* KphDereferenceObject
+ *
+ * Dereferences the specified object. The object will be freed if
+ * its reference count reaches 0.
+ *
+ * Object: A pointer to the object to dereference.
+ *
+ * Return value: TRUE if the object was freed, otherwise FALSE.
+ *
+ * IRQL: <= APC_LEVEL
+ */
+BOOLEAN KphDereferenceObject(
+ __in PVOID Object
+ )
+{
+ return KphDereferenceObjectEx(Object, 1, FALSE) == 0;
+}
+
+/* KphDereferenceObjectDeferDelete
+ *
+ * Dereferences the specified object. The object will be freed in
+ * a worker thread if its reference count reaches 0.
+ *
+ * Object: A pointer to the object to dereference.
+ *
+ * Return value: TRUE if the object was freed, otherwise FALSE.
+ *
+ * IRQL: <= DISPATCH_LEVEL if the object was allocated using the
+ * non-paged pool, otherwise <= APC_LEVEL.
+ */
+BOOLEAN KphDereferenceObjectDeferDelete(
+ __in PVOID Object
+ )
+{
+ return KphDereferenceObjectEx(Object, 1, TRUE) == 0;
+}
+
+/* KphDereferenceObjectEx
+ *
+ * Dereferences the specified object. The object will be freed if
+ * its reference count reaches 0.
+ *
+ * Object: A pointer to the object to dereference.
+ * RefCount: The number of references to remove.
+ *
+ * Return value: The new reference count of the object.
+ *
+ * IRQL: <= DISPATCH_LEVEL if the object was allocated using the
+ * non-paged pool and deletion is being deferred, otherwise <= APC_LEVEL.
+ */
+LONG KphDereferenceObjectEx(
+ __in PVOID Object,
+ __in LONG RefCount,
+ __in BOOLEAN DeferDelete
+ )
+{
+ PKPH_OBJECT_HEADER objectHeader;
+ LONG oldRefCount;
+
+ /* Make sure we're not subtracting a negative reference count. */
+ if (RefCount < 0)
+ ExRaiseStatus(STATUS_INVALID_PARAMETER_2);
+
+ objectHeader = KphObjectToObjectHeader(Object);
+
+ /* Decrease the reference count. */
+ oldRefCount = InterlockedExchangeAdd(&objectHeader->RefCount, -RefCount);
+
+ /* Free the object if it has 0 references. */
+ if (oldRefCount - RefCount == 0)
+ {
+ /* If we are at DISPATCH_LEVEL or higher, the type requests
+ * us to do so, or the caller requests us to do so, defer
+ * the deletion.
+ */
+ if (
+ DeferDelete ||
+ (objectHeader->Type->Flags & KPHOBJTYPE_PASSIVE_LEVEL_DELETE) ||
+ (KeGetCurrentIrql() > APC_LEVEL)
+ )
+ {
+ KphpDeferDeleteObject(objectHeader);
+ }
+ else
+ {
+ /* Free the object. */
+ KphpFreeObject(objectHeader);
+ }
+ }
+
+ return oldRefCount - RefCount;
+}
+
+/* KphGetObjectType
+ *
+ * Gets an object's type.
+ *
+ * IRQL: <= DISPATCH_LEVEL if the object was allocated using the
+ * non-paged pool, otherwise <= APC_LEVEL.
+ */
+PKPH_OBJECT_TYPE KphGetObjectType(
+ __in PVOID Object
+ )
+{
+ return KphObjectToObjectHeader(Object)->Type;
+}
+
+/* KphReferenceObject
+ *
+ * References the specified object.
+ *
+ * Object: A pointer to the object to reference.
+ *
+ * IRQL: <= DISPATCH_LEVEL if the object was allocated using the
+ * non-paged pool, otherwise <= APC_LEVEL.
+ */
+VOID KphReferenceObject(
+ __in PVOID Object
+ )
+{
+ PKPH_OBJECT_HEADER objectHeader;
+
+ objectHeader = KphObjectToObjectHeader(Object);
+ /* Increment the reference count. */
+ InterlockedIncrement(&objectHeader->RefCount);
+}
+
+/* KphReferenceObjectEx
+ *
+ * References the specified object.
+ *
+ * Object: A pointer to the object to reference.
+ * RefCount: The number of references to add.
+ *
+ * Return value: The new reference count of the object.
+ *
+ * IRQL: <= DISPATCH_LEVEL if the object was allocated using the
+ * non-paged pool, otherwise <= APC_LEVEL.
+ */
+LONG KphReferenceObjectEx(
+ __in PVOID Object,
+ __in LONG RefCount
+ )
+{
+ PKPH_OBJECT_HEADER objectHeader;
+ LONG oldRefCount;
+
+ /* Make sure we're not adding a negative reference count. */
+ if (RefCount < 0)
+ ExRaiseStatus(STATUS_INVALID_PARAMETER_2);
+
+ objectHeader = KphObjectToObjectHeader(Object);
+ /* Increase the reference count. */
+ oldRefCount = InterlockedExchangeAdd(&objectHeader->RefCount, RefCount);
+
+ return oldRefCount + RefCount;
+}
+
+/* KphReferenceObjectSafe
+ *
+ * Attempts to reference an object and fails if it is being
+ * destroyed.
+ *
+ * Object: The object to reference if it is not being deleted.
+ *
+ * Return value: TRUE if the object was referenced, FALSE if
+ * it was being deleted and was not referenced.
+ *
+ * Remarks:
+ * This function is useful if a reference to an object is
+ * held, protected by a mutex, and the delete procedure of
+ * the object's type attempts to acquire the mutex. If this
+ * function is called while the mutex is owned, you can
+ * avoid referencing an object that is being destroyed.
+ *
+ * IRQL: <= DISPATCH_LEVEL if the object was allocated using the
+ * non-paged pool, otherwise <= APC_LEVEL.
+ */
+BOOLEAN KphReferenceObjectSafe(
+ __in PVOID Object
+ )
+{
+ PKPH_OBJECT_HEADER objectHeader;
+ BOOLEAN result;
+
+ objectHeader = KphObjectToObjectHeader(Object);
+ /* Increase the reference count only if it isn't 0 (atomically). */
+ result = KphpInterlockedIncrementSafe(&objectHeader->RefCount);
+
+ return result;
+}
+
+/* KphpAllocateObject
+ *
+ * Allocates storage for an object.
+ *
+ * ObjectSize: The size of the object, excluding the header.
+ * PoolType: The pool in which to allocate the object.
+ */
+PKPH_OBJECT_HEADER KphpAllocateObject(
+ __in SIZE_T ObjectSize,
+ __in POOL_TYPE PoolType
+ )
+{
+ return ExAllocatePoolWithTag(
+ PoolType,
+ KphpAddObjectHeaderSize(ObjectSize),
+ TAG_KPHOBJ
+ );
+}
+
+/* KphpDeferDeleteObject
+ *
+ * Queues an object for deletion.
+ *
+ * IRQL: <= DISPATCH_LEVEL if the object was allocated using the
+ * non-paged pool, otherwise <= APC_LEVEL.
+ */
+VOID KphpDeferDeleteObject(
+ __in PKPH_OBJECT_HEADER ObjectHeader
+ )
+{
+ PKPH_OBJECT_HEADER nextToFree;
+
+ /* Add the object to the list while saving the old value, atomically.
+ * Note that it is first-in, last-out.
+ */
+ while (TRUE)
+ {
+ nextToFree = KphObjectNextToFree;
+ ObjectHeader->NextToFree = nextToFree;
+
+ /* Attempt to set the global next-to-free variable. */
+ if (InterlockedCompareExchangePointer(
+ &KphObjectNextToFree,
+ ObjectHeader,
+ nextToFree
+ ) == nextToFree)
+ {
+ /* Success. */
+ break;
+ }
+
+ /* Someone else changed the next-to-free variable.
+ * Go back and try again.
+ */
+ }
+
+ /* Was the to-free list empty before? If so, we need to queue
+ * the work item.
+ */
+ if (!nextToFree)
+ {
+ ExQueueWorkItem(&KphObjectDeferDeleteWorkItem, CriticalWorkQueue);
+ }
+}
+
+/* KphpDeferDeleteObjectRoutine
+ *
+ * Removes and frees objects from the to-free list.
+ *
+ * IRQL: PASSIVE_LEVEL
+ */
+VOID KphpDeferDeleteObjectRoutine(
+ __in PVOID Parameter
+ )
+{
+ PKPH_OBJECT_HEADER objectHeader = NULL;
+
+ while (TRUE)
+ {
+ /* Get the next object to free while replacing the global variable with
+ * what we needed to free next.
+ */
+ objectHeader = InterlockedExchangePointer(&KphObjectNextToFree, objectHeader);
+
+ /* If we have an object to free, free it and move on to the
+ * next object. Otherwise, stop.
+ */
+ if (objectHeader)
+ {
+ KphpFreeObject(objectHeader);
+ objectHeader = objectHeader->NextToFree;
+ }
+ else
+ {
+ break;
+ }
+ }
+}
+
+/* KphpFreeObject
+ *
+ * Calls the delete procedure for an object and frees its
+ * allocated storage.
+ *
+ * ObjectHeader: A pointer to the object header of an allocated object.
+ */
+VOID KphpFreeObject(
+ __in PKPH_OBJECT_HEADER ObjectHeader
+ )
+{
+ /* Object type statistics. */
+ InterlockedDecrement(&ObjectHeader->Type->NumberOfObjects);
+
+ /* Remove the object from the global object list.
+ * If the object manager is being destroyed, don't do this -
+ * we will deadlock because the deinitialization function
+ * holds the mutex.
+ */
+ if (!KphObjectDeinitializing)
+ {
+ ExAcquireFastMutex(&KphObjectListMutex);
+ RemoveEntryList(&ObjectHeader->GlobalObjectListEntry);
+ ExReleaseFastMutex(&KphObjectListMutex);
+ }
+
+ /* Call the delete procedure if we have one. */
+ if (ObjectHeader->Type->DeleteProcedure)
+ {
+ ObjectHeader->Type->DeleteProcedure(
+ KphObjectHeaderToObject(ObjectHeader),
+ ObjectHeader->Flags
+ );
+ }
+
+ ExFreePoolWithTag(
+ ObjectHeader,
+ TAG_KPHOBJ
+ );
+}
diff --git a/branches/ph-plugins/KProcessHacker/resource.rc b/branches/ph-plugins/KProcessHacker/resource.rc
new file mode 100644
index 000000000..e02647e28
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/resource.rc
@@ -0,0 +1,53 @@
+#include
+
+#define VER_COMMA 1,6,0,0
+#define VER_STR "1.6\0"
+
+#define VER_FILEVERSION VER_COMMA
+#define VER_FILEVERSION_STR VER_STR
+#define VER_PRODUCTVERSION VER_COMMA
+#define VER_PRODUCTVERSION_STR VER_STR
+
+#ifndef DEBUG
+#define VER_DEBUG 0
+#else
+#define VER_DEBUG VS_FF_DEBUG
+#endif
+
+#define VER_PRIVATEBUILD 0
+#define VER_PRERELEASE 0
+
+#define VER_COMPANYNAME_STR "wj32\0"
+#define VER_FILEDESCRIPTION_STR "KProcessHacker\0"
+#define VER_LEGALCOPYRIGHT_STR "Copyright (c) 2009 wj32. Licensed under the GNU GPL, v3.\0"
+#define VER_ORIGINALFILENAME_STR "kprocesshacker.sys\0"
+#define VER_PRODUCTNAME_STR "KProcessHacker\0"
+
+VS_VERSION_INFO VERSIONINFO
+FILEVERSION VER_FILEVERSION
+PRODUCTVERSION VER_PRODUCTVERSION
+FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+FILEFLAGS (VER_PRIVATEBUILD | VER_PRERELEASE | VER_DEBUG)
+FILEOS VOS__WINDOWS32
+FILETYPE VFT_DRV
+FILESUBTYPE VFT2_DRV_SYSTEM
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904E4"
+ BEGIN
+ VALUE "CompanyName", VER_COMPANYNAME_STR
+ VALUE "FileDescription", VER_FILEDESCRIPTION_STR
+ VALUE "FileVersion", VER_FILEVERSION_STR
+ VALUE "LegalCopyright", VER_LEGALCOPYRIGHT_STR
+ VALUE "OriginalFilename", VER_ORIGINALFILENAME_STR
+ VALUE "ProductName", VER_PRODUCTNAME_STR
+ VALUE "ProductVersion", VER_PRODUCTVERSION_STR
+ END
+ END
+
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1252
+ END
+END
diff --git a/branches/ph-plugins/KProcessHacker/se.c b/branches/ph-plugins/KProcessHacker/se.c
new file mode 100644
index 000000000..ce09c6607
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/se.c
@@ -0,0 +1,102 @@
+/*
+ * Process Hacker Driver -
+ * security
+ *
+ * Copyright (C) 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 .
+ */
+
+#include "include/kph.h"
+#include "include/se.h"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, KphOpenProcessTokenEx)
+#endif
+
+/* KphOpenProcessTokenEx
+ *
+ * Opens the primary token of the specified process.
+ */
+NTSTATUS KphOpenProcessTokenEx(
+ __in HANDLE ProcessHandle,
+ __in ACCESS_MASK DesiredAccess,
+ __in ULONG ObjectAttributes,
+ __out PHANDLE TokenHandle,
+ __in KPROCESSOR_MODE AccessMode
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PEPROCESS processObject;
+ PACCESS_TOKEN tokenObject;
+ HANDLE tokenHandle;
+ ACCESS_STATE accessState;
+ CHAR auxData[AUX_ACCESS_DATA_SIZE];
+
+ status = SeCreateAccessState(
+ &accessState,
+ (PAUX_ACCESS_DATA)auxData,
+ DesiredAccess,
+ (PGENERIC_MAPPING)KVOFF(*SeTokenObjectType, OffOtiGenericMapping)
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ return status;
+ }
+
+ if (accessState.RemainingDesiredAccess & MAXIMUM_ALLOWED)
+ accessState.PreviouslyGrantedAccess |= TOKEN_ALL_ACCESS;
+ else
+ accessState.PreviouslyGrantedAccess |= accessState.RemainingDesiredAccess;
+
+ accessState.RemainingDesiredAccess = 0;
+
+ status = ObReferenceObjectByHandle(
+ ProcessHandle,
+ 0,
+ *PsProcessType,
+ KernelMode,
+ &processObject,
+ NULL
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ SeDeleteAccessState(&accessState);
+ return status;
+ }
+
+ tokenObject = PsReferencePrimaryToken(processObject);
+ ObDereferenceObject(processObject);
+
+ status = ObOpenObjectByPointer(
+ tokenObject,
+ ObjectAttributes,
+ &accessState,
+ 0,
+ *SeTokenObjectType,
+ AccessMode,
+ &tokenHandle
+ );
+ SeDeleteAccessState(&accessState);
+ ObDereferenceObject(tokenObject);
+
+ if (NT_SUCCESS(status))
+ *TokenHandle = tokenHandle;
+
+ return status;
+}
diff --git a/branches/ph-plugins/KProcessHacker/sources b/branches/ph-plugins/KProcessHacker/sources
new file mode 100644
index 000000000..ff6e07b5e
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/sources
@@ -0,0 +1,29 @@
+TARGETNAME=kprocesshacker
+TARGETTYPE=DRIVER
+TARGETPATH=.\
+
+INCLUDES=$(DDK_INC_PATH)
+LIBS=%BUILD%\lib
+
+SOURCES= \
+ kprocesshacker.c \
+ version.c \
+ \
+ kph.c \
+ handle.c \
+ hook.c \
+ protect.c \
+ ref.c \
+ sync.c \
+ sysservice.c \
+ sysservicedata.c \
+ test.c \
+ trace.c \
+ util.c \
+ \
+ io.c \
+ mm.c \
+ ob.c \
+ ps.c \
+ se.c \
+ resource.rc
diff --git a/branches/ph-plugins/KProcessHacker/sync.c b/branches/ph-plugins/KProcessHacker/sync.c
new file mode 100644
index 000000000..99bd46bf8
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/sync.c
@@ -0,0 +1,312 @@
+/*
+ * Process Hacker Driver -
+ * synchronization code
+ *
+ * Copyright (C) 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 .
+ */
+
+#include "include/sync.h"
+#include "include/debug.h"
+
+ULONG KphpCountBits(
+ __in ULONG_PTR Number
+ );
+
+VOID KphpProcessorLockDpc(
+ __in PKDPC Dpc,
+ __in PVOID DeferredContext,
+ __in PVOID SystemArgument1,
+ __in PVOID SystemArgument2
+ );
+
+/* KphfAcquireGuardedLock
+ *
+ * Acquires a guarded lock and raises the IRQL to APC_LEVEL.
+ *
+ * IRQL: <= APC_LEVEL
+ */
+VOID FASTCALL KphfAcquireGuardedLock(
+ __inout PKPH_GUARDED_LOCK Lock
+ )
+{
+ KIRQL oldIrql;
+
+ ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
+
+ /* Raise to APC_LEVEL. */
+ oldIrql = KeRaiseIrql(APC_LEVEL, &oldIrql);
+
+ /* Acquire the spinlock. */
+ KphAcquireBitSpinLock(&Lock->Value, KPH_GUARDED_LOCK_ACTIVE_SHIFT);
+
+ /* Now that we have the lock, we must save the old IRQL. */
+ /* Clear the old IRQL. */
+ Lock->Value &= KPH_GUARDED_LOCK_FLAGS;
+ /* Set the new IRQL. */
+ Lock->Value |= oldIrql;
+}
+
+/* KphfReleaseGuardedLock
+ *
+ * Releases a guarded lock and restores the old IRQL.
+ *
+ * IRQL: >= APC_LEVEL
+ */
+VOID FASTCALL KphfReleaseGuardedLock(
+ __inout PKPH_GUARDED_LOCK Lock
+ )
+{
+ KIRQL oldIrql;
+
+ ASSERT(KeGetCurrentIrql() >= APC_LEVEL);
+
+ /* Get the old IRQL. */
+ oldIrql = (KIRQL)(Lock->Value & ~KPH_GUARDED_LOCK_FLAGS);
+ /* Unlock the spinlock. */
+ KphReleaseBitSpinLock(&Lock->Value, KPH_GUARDED_LOCK_ACTIVE_SHIFT);
+ /* Restore the old IRQL. */
+ KeLowerIrql(oldIrql);
+}
+
+/* KphAcquireProcessorLock
+ *
+ * Raises the IRQL to DISPATCH_LEVEL and prevents threads from
+ * executing on other processors until the processor lock is released.
+ * Blocks if the supplied processor lock is already in use.
+ *
+ * ProcessorLock: A processor lock structure that is present in
+ * non-paged memory.
+ *
+ * Comments:
+ * Here is how the processor lock works:
+ * 1. Tries to acquire the mutex in the processor lock, and
+ * blocks until it can be obtained.
+ * 2. Initializes a DPC for each processor on the computer.
+ * 3. Raises the IRQL to DISPATCH_LEVEL to make sure the
+ * code is not interrupted by a context switch.
+ * 4. Queues each of the previously-initialized DPCs, except if
+ * it is targeted at the current processor.
+ * 5. Since DPCs run at DISPATCH_LEVEL, they have exclusive
+ * control of the processor. As each runs, they increment
+ * a counter in the processor lock. They then enter a loop.
+ * 6. The routine waits for the counter to become n - 1,
+ * signaling that all (other) processors have been acquired
+ * (where n is the number of processors).
+ * 7. It returns. Any code from here will be running in
+ * DISPATCH_LEVEL and will be the only code running on the
+ * machine.
+ * Thread safety: Full
+ * IRQL: <= APC_LEVEL
+ */
+BOOLEAN KphAcquireProcessorLock(
+ __inout PKPH_PROCESSOR_LOCK ProcessorLock
+ )
+{
+ ULONG i;
+ ULONG numberProcessors;
+ ULONG currentProcessor;
+
+ /* Acquire the processor lock guarded lock. */
+ KphAcquireGuardedLock(&ProcessorLock->Lock);
+
+ /* Reset some state. */
+ ASSERT(ProcessorLock->AcquiredProcessors == 0);
+ ProcessorLock->AcquiredProcessors = 0;
+ ProcessorLock->ReleaseSignal = 0; /* IMPORTANT */
+
+ /* Get the number of processors. */
+ numberProcessors = KphpCountBits(KeQueryActiveProcessors());
+
+ /* If there's only one processor we can simply raise the IRQL and exit. */
+ if (numberProcessors == 1)
+ {
+ dprintf("KphAcquireProcessorLock: Only one processor, raising IRQL and exiting...\n");
+ KeRaiseIrql(DISPATCH_LEVEL, &ProcessorLock->OldIrql);
+ ProcessorLock->Acquired = TRUE;
+
+ return TRUE;
+ }
+
+ /* Allocate storage for the DPCs. */
+ ProcessorLock->Dpcs = ExAllocatePoolWithTag(
+ NonPagedPool,
+ sizeof(KDPC) * numberProcessors,
+ TAG_SYNC_DPC
+ );
+
+ if (!ProcessorLock->Dpcs)
+ {
+ dprintf("KphAcquireProcessorLock: Could not allocate storage for DPCs!\n");
+ KphReleaseGuardedLock(&ProcessorLock->Lock);
+ return FALSE;
+ }
+
+ /* Initialize the DPCs. */
+ for (i = 0; i < numberProcessors; i++)
+ {
+ KeInitializeDpc(&ProcessorLock->Dpcs[i], KphpProcessorLockDpc, NULL);
+ KeSetTargetProcessorDpc(&ProcessorLock->Dpcs[i], (CCHAR)i);
+ KeSetImportanceDpc(&ProcessorLock->Dpcs[i], HighImportance);
+ }
+
+ /* Raise the IRQL to DISPATCH_LEVEL to prevent context switching. */
+ KeRaiseIrql(DISPATCH_LEVEL, &ProcessorLock->OldIrql);
+ /* Get the current processor number. */
+ currentProcessor = KeGetCurrentProcessorNumber();
+
+ /* Queue the DPCs (except on the current processor). */
+ for (i = 0; i < numberProcessors; i++)
+ if (i != currentProcessor)
+ KeInsertQueueDpc(&ProcessorLock->Dpcs[i], ProcessorLock, NULL);
+
+ /* Spinwait for all (other) processors to be acquired. */
+ KphSpinUntilEqual(&ProcessorLock->AcquiredProcessors, numberProcessors - 1);
+
+ dprintf("KphAcquireProcessorLock: All processors acquired.\n");
+ ProcessorLock->Acquired = TRUE;
+
+ return TRUE;
+}
+
+/* KphInitializeProcessorLock
+ *
+ * Initializes a processor lock.
+ *
+ * ProcessorLock: A processor lock structure that is present in
+ * non-paged memory.
+ *
+ * IRQL: Any
+ */
+VOID KphInitializeProcessorLock(
+ __out PKPH_PROCESSOR_LOCK ProcessorLock
+ )
+{
+ KphInitializeGuardedLock(&ProcessorLock->Lock, FALSE);
+ ProcessorLock->Dpcs = NULL;
+ ProcessorLock->AcquiredProcessors = 0;
+ ProcessorLock->ReleaseSignal = 0;
+ ProcessorLock->OldIrql = PASSIVE_LEVEL;
+ ProcessorLock->Acquired = FALSE;
+}
+
+/* KphReleaseProcessorLock
+ *
+ * Allows threads to execute on other processors and restores the IRQL.
+ *
+ * ProcessorLock: A processor lock structure that is present in
+ * non-paged memory.
+ *
+ * Comments:
+ * Here is how the processor lock is released:
+ * 1. Sets the signal to release the processors. The DPCs that are
+ * currently waiting for the signal will return and decrement
+ * the acquired processors counter.
+ * 2. Waits for the acquired processors counter to become zero.
+ * 3. Restores the old IRQL. This will always be APC_LEVEL due to
+ * the mutex.
+ * 4. Frees the storage allocated for the DPCs.
+ * 5. Releases the processor lock mutex. This will restore the IRQL
+ * back to normal.
+ * Thread safety: Full
+ * IRQL: DISPATCH_LEVEL
+ */
+VOID KphReleaseProcessorLock(
+ __inout PKPH_PROCESSOR_LOCK ProcessorLock
+ )
+{
+ if (!ProcessorLock->Acquired)
+ return;
+
+ /* Signal for the acquired processors to be released. */
+ InterlockedExchange(&ProcessorLock->ReleaseSignal, 1);
+
+ /* Spinwait for all acquired processors to be released. */
+ KphSpinUntilEqual(&ProcessorLock->AcquiredProcessors, 0);
+
+ dprintf("KphReleaseProcessorLock: All processors released.\n");
+
+ /* Restore the old IRQL (should always be APC_LEVEL due to the
+ * fast mutex). */
+ KeLowerIrql(ProcessorLock->OldIrql);
+
+ /* Free the DPCs if necessary. */
+ if (ProcessorLock->Dpcs != NULL)
+ {
+ ExFreePoolWithTag(ProcessorLock->Dpcs, TAG_SYNC_DPC);
+ ProcessorLock->Dpcs = NULL;
+ }
+
+ ProcessorLock->Acquired = FALSE;
+
+ /* Release the processor lock guarded lock. This will restore the
+ * IRQL back to what it was before the processor lock was
+ * acquired.
+ */
+ KphReleaseGuardedLock(&ProcessorLock->Lock);
+}
+
+/* KphpCountBits
+ *
+ * Counts the number of bits set in an integer.
+ */
+ULONG KphpCountBits(
+ __in ULONG_PTR Number
+ )
+{
+ ULONG count = 0;
+
+ while (Number)
+ {
+ count++;
+ Number &= Number - 1;
+ }
+
+ return count;
+}
+
+/* KphpProcessorLockDpc
+ *
+ * The DPC routine which "locks" processors.
+ *
+ * Thread safety: Full
+ * IRQL: DISPATCH_LEVEL
+ */
+VOID KphpProcessorLockDpc(
+ __in PKDPC Dpc,
+ __in PVOID DeferredContext,
+ __in PVOID SystemArgument1,
+ __in PVOID SystemArgument2
+ )
+{
+ PKPH_PROCESSOR_LOCK processorLock = (PKPH_PROCESSOR_LOCK)SystemArgument1;
+
+ ASSERT(processorLock != NULL);
+
+ dprintf("KphpProcessorLockDpc: Acquiring processor %d.\n", KeGetCurrentProcessorNumber());
+
+ /* Increase the number of acquired processors. */
+ InterlockedIncrement(&processorLock->AcquiredProcessors);
+
+ /* Spin until we get the signal to release the processor. */
+ KphSpinUntilNotEqual(&processorLock->ReleaseSignal, 0);
+
+ /* Decrease the number of acquired processors. */
+ InterlockedDecrement(&processorLock->AcquiredProcessors);
+
+ dprintf("KphpProcessorLockDpc: Releasing processor %d.\n", KeGetCurrentProcessorNumber());
+}
diff --git a/branches/ph-plugins/KProcessHacker/sysservice.c b/branches/ph-plugins/KProcessHacker/sysservice.c
new file mode 100644
index 000000000..0568e7d07
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/sysservice.c
@@ -0,0 +1,2050 @@
+/*
+ * Process Hacker Driver -
+ * system service logging
+ *
+ * Copyright (C) 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 .
+ */
+
+/* ================ IMPORTANT ================
+ * Please read the comments in KphpSsNewKiFastCallEntry to find out how
+ * KiFastCallEntry can be hooked.
+ *
+ * Note that the ONLY SUPPORTED METHOD of hooking is KiFastCallEntry,
+ * which means you MUST be using a CPU which supports sysenter.
+ * ===========================================
+ */
+
+#include "include/sysservicep.h"
+#include "include/hook.h"
+#include "include/sync.h"
+#include "include/trace.h"
+
+extern PDRIVER_OBJECT KphDriverObject;
+
+/* A fast mutex guarding starting/stopping system service logging. */
+FAST_MUTEX KphSsMutex;
+/* Whether system service logging has been initialized. */
+BOOLEAN KphSsInitialized = FALSE;
+/* The KiFastCallEntry hook. */
+KPH_HOOK KphSsKiFastCallEntryHook;
+/* The number of active loggers. */
+ULONG KphSsNumberOfActiveLoggers = 0;
+
+/* The object type for client entries. */
+PKPH_OBJECT_TYPE KphSsClientEntryType;
+/* The object type for ruleset entries. */
+PKPH_OBJECT_TYPE KphSsRuleSetEntryType;
+/* The object type for rule entries. */
+PKPH_OBJECT_TYPE KphSsRuleEntryType;
+
+/* The list of ruleset entries. */
+LIST_ENTRY KphSsRuleSetListHead;
+/* A push lock guarding accesses to the ruleset list. */
+EX_PUSH_LOCK KphSsRuleSetListPushLock;
+
+/* KphSsLogInit
+ *
+ * Initializes system service logging.
+ */
+NTSTATUS KphSsLogInit()
+{
+ NTSTATUS status = STATUS_SUCCESS;
+
+ /* Initialize the system service call data. */
+ KphSsDataInit();
+
+ /* Initialize the ruleset list. */
+ InitializeListHead(&KphSsRuleSetListHead);
+ ExInitializeFastMutex(&KphSsMutex);
+ ExInitializePushLock(&KphSsRuleSetListPushLock);
+
+ /* Initialize the object types. */
+ status = KphCreateObjectType(
+ &KphSsClientEntryType,
+ NonPagedPool,
+ 0,
+ KphpSsClientEntryDeleteProcedure
+ );
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ status = KphCreateObjectType(
+ &KphSsRuleSetEntryType,
+ NonPagedPool,
+ 0,
+ KphpSsRuleSetEntryDeleteProcedure
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ KphDereferenceObject(KphSsClientEntryType);
+ return status;
+ }
+
+ status = KphCreateObjectType(
+ &KphSsRuleEntryType,
+ NonPagedPool,
+ 0,
+ NULL
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ KphDereferenceObject(KphSsClientEntryType);
+ KphDereferenceObject(KphSsRuleSetEntryType);
+ return status;
+ }
+
+ return status;
+}
+
+/* KphSsLogDeinit
+ *
+ * Frees system service logging data.
+ */
+NTSTATUS KphSsLogDeinit()
+{
+ KphSsDataDeinit();
+
+ return STATUS_SUCCESS;
+}
+
+/* KphSsLogStart
+ *
+ * Starts system service logging.
+ */
+NTSTATUS KphSsLogStart()
+{
+#ifdef _X86_
+ NTSTATUS status = STATUS_SUCCESS;
+
+ /* Make sure we have the KiFastCallEntry+x address. */
+ if (!__KiFastCallEntry)
+ return STATUS_NOT_SUPPORTED;
+
+ ExAcquireFastMutex(&KphSsMutex);
+
+ if (KphSsInitialized)
+ {
+ ExReleaseFastMutex(&KphSsMutex);
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ /* Hook KiFastCallEntry. Logging will start from now. */
+ KphInitializeHook(
+ &KphSsKiFastCallEntryHook,
+ __KiFastCallEntry,
+ KphpSsNewKiFastCallEntry
+ );
+ status = KphHook(&KphSsKiFastCallEntryHook);
+
+ if (!NT_SUCCESS(status))
+ {
+ ExReleaseFastMutex(&KphSsMutex);
+ return status;
+ }
+
+ KphSsInitialized = TRUE;
+
+ ExReleaseFastMutex(&KphSsMutex);
+
+ return status;
+#else
+ return STATUS_NOT_SUPPORTED;
+#endif
+}
+
+/* KphSsLogStop
+ *
+ * Stops system service logging.
+ */
+NTSTATUS KphSsLogStop()
+{
+#ifdef _X86_
+ NTSTATUS status = STATUS_SUCCESS;
+
+ ExAcquireFastMutex(&KphSsMutex);
+
+ if (!KphSsInitialized)
+ {
+ ExReleaseFastMutex(&KphSsMutex);
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ status = KphUnhook(&KphSsKiFastCallEntryHook);
+
+ if (!NT_SUCCESS(status))
+ {
+ ExReleaseFastMutex(&KphSsMutex);
+ return status;
+ }
+
+ /* Spin until the logger count reaches 0. */
+ KphSpinUntilEqual(&KphSsNumberOfActiveLoggers, 0);
+
+ KphSsInitialized = FALSE;
+
+ ExReleaseFastMutex(&KphSsMutex);
+
+ return status;
+#else
+ return STATUS_NOT_SUPPORTED;
+#endif
+}
+
+/* KphSsCreateClientEntry
+ *
+ * Creates a client entry which describes a client of the
+ * system service logger. Clients receive system service log events.
+ * Note that a client may have several ruleset entries associated
+ * with it.
+ *
+ * ClientEntry: A variable which receives a pointer to the client entry.
+ * ProcessHandle: A handle to the client process, with PROCESS_VM_WRITE
+ * access.
+ * ReadSemaphoreHandle: A handle to a semaphore which is released when an
+ * event is written to the client buffer. The client must wait for the
+ * semaphore when it is about to read a block.
+ * WriteSemaphoreHandle: A handle to a semaphore which is acquired when an
+ * event is about to be written to the client buffer. If the semaphore
+ * cannot be acquired immediately, the event is dropped. The client must
+ * continually read the buffer and release the semaphore.
+ * BufferBase: A pointer to a buffer in the client process.
+ * BufferSize: The size of the buffer, in bytes.
+ * AccessMode: The mode to use when probing arguments.
+ */
+NTSTATUS KphSsCreateClientEntry(
+ __out PKPHSS_CLIENT_ENTRY *ClientEntry,
+ __in HANDLE ProcessHandle,
+ __in HANDLE ReadSemaphoreHandle,
+ __in HANDLE WriteSemaphoreHandle,
+ __in PVOID BufferBase,
+ __in ULONG BufferSize,
+ __in KPROCESSOR_MODE AccessMode
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PKPHSS_CLIENT_ENTRY clientEntry;
+ PEPROCESS processObject;
+ PKSEMAPHORE readSemaphore;
+ PKSEMAPHORE writeSemaphore;
+
+ /* Probe. */
+ if (AccessMode != KernelMode)
+ {
+ __try
+ {
+ ProbeForWrite(BufferBase, BufferSize, 1);
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ return GetExceptionCode();
+ }
+ }
+
+ /* Reference the client process. */
+ status = ObReferenceObjectByHandle(
+ ProcessHandle,
+ PROCESS_VM_WRITE,
+ *PsProcessType,
+ AccessMode,
+ &processObject,
+ NULL
+ );
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ /* Reference the read semaphore. */
+ status = ObReferenceObjectByHandle(
+ ReadSemaphoreHandle,
+ SEMAPHORE_MODIFY_STATE,
+ *ExSemaphoreObjectType,
+ AccessMode,
+ &readSemaphore,
+ NULL
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ ObDereferenceObject(processObject);
+ return status;
+ }
+
+ /* Reference the write semaphore. */
+ status = ObReferenceObjectByHandle(
+ WriteSemaphoreHandle,
+ SEMAPHORE_MODIFY_STATE,
+ *ExSemaphoreObjectType,
+ AccessMode,
+ &writeSemaphore,
+ NULL
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ ObDereferenceObject(processObject);
+ ObDereferenceObject(readSemaphore);
+ return status;
+ }
+
+ /* Create the client entry object. */
+ status = KphCreateObject(
+ &clientEntry,
+ sizeof(KPHSS_CLIENT_ENTRY),
+ 0,
+ KphSsClientEntryType,
+ 0
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ ObDereferenceObject(processObject);
+ ObDereferenceObject(readSemaphore);
+ ObDereferenceObject(writeSemaphore);
+
+ return status;
+ }
+
+ clientEntry->Process = processObject;
+ clientEntry->Enabled = TRUE;
+ clientEntry->ReadSemaphore = readSemaphore;
+ clientEntry->WriteSemaphore = writeSemaphore;
+ ExInitializeFastMutex(&clientEntry->BufferMutex);
+ clientEntry->BufferBase = BufferBase;
+ clientEntry->BufferSize = BufferSize;
+ clientEntry->BufferCursor = 0;
+ clientEntry->NumberOfBlocksWritten = 0;
+ clientEntry->NumberOfBlocksDropped = 0;
+
+ *ClientEntry = clientEntry;
+
+ return status;
+}
+
+/* KphSsEnableClientEntry
+ *
+ * Enables or disables a client entry.
+ */
+NTSTATUS KphSsEnableClientEntry(
+ __in PKPHSS_CLIENT_ENTRY ClientEntry,
+ __in BOOLEAN Enable
+ )
+{
+ if (Enable)
+ ClientEntry->Enabled = TRUE;
+ else
+ ClientEntry->Enabled = FALSE;
+
+ return STATUS_SUCCESS;
+}
+
+/* KphSsQueryClientEntry
+ *
+ * Queries information about a client entry.
+ */
+NTSTATUS KphSsQueryClientEntry(
+ __in PKPHSS_CLIENT_ENTRY ClientEntry,
+ __out_bcount_opt(ClientInformationLength) PKPHSS_CLIENT_INFORMATION ClientInformation,
+ __in ULONG ClientInformationLength,
+ __out_opt PULONG ReturnLength,
+ __in KPROCESSOR_MODE AccessMode
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+
+ /* Probe the return length if necessary. */
+ if (AccessMode != KernelMode)
+ {
+ __try
+ {
+ ProbeForWrite(ReturnLength, sizeof(ULONG), 1);
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ return GetExceptionCode();
+ }
+ }
+
+ /* Check the length. */
+ if (ClientInformationLength >= sizeof(KPHSS_CLIENT_INFORMATION))
+ {
+ if (ClientInformation)
+ {
+ __try
+ {
+ /* Probe the buffer if we're not from kernel-mode. */
+ if (AccessMode != KernelMode)
+ ProbeForWrite(ClientInformation, sizeof(KPHSS_CLIENT_INFORMATION), 1);
+
+ ClientInformation->ProcessId = PsGetProcessId(ClientEntry->Process);
+ ClientInformation->BufferBase = ClientEntry->BufferBase;
+ ClientInformation->BufferSize = ClientEntry->BufferSize;
+ ClientInformation->NumberOfBlocksWritten = ClientEntry->NumberOfBlocksWritten;
+ ClientInformation->NumberOfBlocksDropped = ClientEntry->NumberOfBlocksDropped;
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ status = GetExceptionCode();
+ }
+ }
+ }
+ else
+ {
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+
+ /* Pass the return length back if requested. */
+ if (ReturnLength)
+ {
+ __try
+ {
+ *ReturnLength = sizeof(KPHSS_CLIENT_INFORMATION);
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ status = GetExceptionCode();
+ }
+ }
+
+ return status;
+}
+
+/* KphpSsClientEntryDeleteProcedure
+ *
+ * Performs cleanup for a client entry.
+ */
+VOID NTAPI KphpSsClientEntryDeleteProcedure(
+ __in PVOID Object,
+ __in ULONG Flags
+ )
+{
+ PKPHSS_CLIENT_ENTRY clientEntry = (PKPHSS_CLIENT_ENTRY)Object;
+
+ ObDereferenceObject(clientEntry->Process);
+ ObDereferenceObject(clientEntry->ReadSemaphore);
+ ObDereferenceObject(clientEntry->WriteSemaphore);
+}
+
+/* KphSsCreateRuleSetEntry
+ *
+ * Creates a ruleset entry which contains a list of rules
+ * and an action to perform.
+ */
+NTSTATUS KphSsCreateRuleSetEntry(
+ __out PKPHSS_RULESET_ENTRY *RuleSetEntry,
+ __in PKPHSS_CLIENT_ENTRY ClientEntry,
+ __in KPHSS_FILTER_TYPE DefaultFilterType,
+ __in KPHSS_RULESET_ACTION Action
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PKPHSS_RULESET_ENTRY ruleSetEntry;
+
+ /* Make sure the action is valid. */
+ if (Action < LogRuleSetAction || Action >= MaxRuleSetAction)
+ return STATUS_INVALID_PARAMETER_3;
+
+ /* Create the ruleset object. */
+ status = KphCreateObject(
+ &ruleSetEntry,
+ sizeof(KPHSS_RULESET_ENTRY),
+ 0,
+ KphSsRuleSetEntryType,
+ 0
+ );
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ /* Initialize the ruleset object. */
+ KphReferenceObject(ClientEntry);
+ ruleSetEntry->Client = ClientEntry;
+ ruleSetEntry->DefaultFilterType = DefaultFilterType;
+ ruleSetEntry->Action = Action;
+ ruleSetEntry->NextRuleHandle = 4;
+ ExInitializePushLock(&ruleSetEntry->RuleListPushLock);
+ InitializeListHead(&ruleSetEntry->RuleListHead);
+
+ /* Add the ruleset to the list. */
+ KeEnterCriticalRegion();
+ ExAcquirePushLockExclusive(&KphSsRuleSetListPushLock);
+ InsertHeadList(&KphSsRuleSetListHead, &ruleSetEntry->RuleSetListEntry);
+ ExReleasePushLock(&KphSsRuleSetListPushLock);
+ KeLeaveCriticalRegion();
+
+ *RuleSetEntry = ruleSetEntry;
+
+ return status;
+}
+
+/* KphpSsRuleSetEntryDeleteProcedure
+ *
+ * Performs cleanup for a ruleset entry.
+ */
+VOID NTAPI KphpSsRuleSetEntryDeleteProcedure(
+ __in PVOID Object,
+ __in ULONG Flags
+ )
+{
+ PKPHSS_RULESET_ENTRY ruleSetEntry = (PKPHSS_RULESET_ENTRY)Object;
+ PLIST_ENTRY currentRuleListEntry;
+
+ /* Dereference the client entry. */
+ KphDereferenceObject(ruleSetEntry->Client);
+
+ KeEnterCriticalRegion();
+
+ /* Dereference all rules in the ruleset. */
+ ExAcquirePushLockExclusive(&ruleSetEntry->RuleListPushLock);
+
+ currentRuleListEntry = ruleSetEntry->RuleListHead.Flink;
+
+ while (currentRuleListEntry != &ruleSetEntry->RuleListHead)
+ {
+ PLIST_ENTRY nextEntry;
+
+ /* Save the next entry pointer since currentRuleListEntry may
+ * be deallocated due to the dereference.
+ */
+ nextEntry = currentRuleListEntry->Flink;
+ KphDereferenceObject(KPHSS_RULE_ENTRY(currentRuleListEntry));
+ currentRuleListEntry = nextEntry;
+ }
+
+ ExReleasePushLock(&ruleSetEntry->RuleListPushLock);
+
+ /* Remove the ruleset from the list. */
+ ExAcquirePushLockExclusive(&KphSsRuleSetListPushLock);
+ RemoveEntryList(&ruleSetEntry->RuleSetListEntry);
+ ExReleasePushLock(&KphSsRuleSetListPushLock);
+
+ KeLeaveCriticalRegion();
+}
+
+/* KphSsAddProcessIdRule
+ *
+ * Adds a process ID rule entry to a ruleset entry.
+ */
+NTSTATUS KphSsAddProcessIdRule(
+ __out PKPHSS_RULE_ENTRY *RuleEntry,
+ __in PKPHSS_RULESET_ENTRY RuleSetEntry,
+ __in KPHSS_FILTER_TYPE FilterType,
+ __in HANDLE ProcessId
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PKPHSS_RULE_ENTRY ruleEntry;
+
+ /* Add the rule. */
+ status = KphpSsAddRule(&ruleEntry, RuleSetEntry, FilterType, ProcessIdRuleType);
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ ruleEntry->ProcessIdRule.ProcessId = ProcessId;
+ ruleEntry->Initialized = TRUE;
+
+ *RuleEntry = ruleEntry;
+
+ return status;
+}
+
+/* KphSsAddThreadIdRule
+ *
+ * Adds a thread ID rule entry to a ruleset entry.
+ */
+NTSTATUS KphSsAddThreadIdRule(
+ __out PKPHSS_RULE_ENTRY *RuleEntry,
+ __in PKPHSS_RULESET_ENTRY RuleSetEntry,
+ __in KPHSS_FILTER_TYPE FilterType,
+ __in HANDLE ThreadId
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PKPHSS_RULE_ENTRY ruleEntry;
+
+ /* Add the rule. */
+ status = KphpSsAddRule(&ruleEntry, RuleSetEntry, FilterType, ThreadIdRuleType);
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ ruleEntry->ThreadIdRule.ThreadId = ThreadId;
+ ruleEntry->Initialized = TRUE;
+
+ *RuleEntry = ruleEntry;
+
+ return status;
+}
+
+/* KphSsAddPreviousModeRule
+ *
+ * Adds a previous mode rule entry to a ruleset entry.
+ */
+NTSTATUS KphSsAddPreviousModeRule(
+ __out PKPHSS_RULE_ENTRY *RuleEntry,
+ __in PKPHSS_RULESET_ENTRY RuleSetEntry,
+ __in KPHSS_FILTER_TYPE FilterType,
+ __in KPROCESSOR_MODE PreviousMode
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PKPHSS_RULE_ENTRY ruleEntry;
+
+ /* Add the rule. */
+ status = KphpSsAddRule(&ruleEntry, RuleSetEntry, FilterType, PreviousModeRuleType);
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ ruleEntry->PreviousModeRule.PreviousMode = PreviousMode;
+ ruleEntry->Initialized = TRUE;
+
+ *RuleEntry = ruleEntry;
+
+ return status;
+}
+
+/* KphSsAddNumberRule
+ *
+ * Adds a system service number rule entry to a ruleset entry.
+ */
+NTSTATUS KphSsAddNumberRule(
+ __out PKPHSS_RULE_ENTRY *RuleEntry,
+ __in PKPHSS_RULESET_ENTRY RuleSetEntry,
+ __in KPHSS_FILTER_TYPE FilterType,
+ __in ULONG Number
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PKPHSS_RULE_ENTRY ruleEntry;
+
+ /* Add the rule. */
+ status = KphpSsAddRule(&ruleEntry, RuleSetEntry, FilterType, NumberRuleType);
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ ruleEntry->NumberRule.Number = Number;
+ ruleEntry->Initialized = TRUE;
+
+ *RuleEntry = ruleEntry;
+
+ return status;
+}
+
+/* KphSsGetHandleRule
+ *
+ * Gets the handle of a rule.
+ */
+HANDLE KphSsGetHandleRule(
+ __in PKPHSS_RULE_ENTRY RuleEntry
+ )
+{
+ return RuleEntry->Handle;
+}
+
+/* KphSsRemoveRule
+ *
+ * Removes a rule entry from a ruleset entry.
+ */
+NTSTATUS KphSsRemoveRule(
+ __in PKPHSS_RULESET_ENTRY RuleSetEntry,
+ __in HANDLE RuleEntryHandle
+ )
+{
+ PLIST_ENTRY currentListEntry;
+
+ KeEnterCriticalRegion();
+ ExAcquirePushLockExclusive(&RuleSetEntry->RuleListPushLock);
+
+ /* Find the rule in the ruleset. */
+
+ currentListEntry = RuleSetEntry->RuleListHead.Flink;
+
+ while (currentListEntry != &RuleSetEntry->RuleListHead)
+ {
+ PKPHSS_RULE_ENTRY ruleEntry = KPHSS_RULE_ENTRY(currentListEntry);
+
+ if (ruleEntry->Handle == RuleEntryHandle)
+ {
+ /* Remove the rule from the list. */
+ RemoveEntryList(&ruleEntry->RuleListEntry);
+ /* Dereference the rule (it was referenced when it
+ * got added to the list).
+ */
+ KphDereferenceObject(ruleEntry);
+
+ ExReleasePushLock(&RuleSetEntry->RuleListPushLock);
+ KeLeaveCriticalRegion();
+
+ return STATUS_SUCCESS;
+ }
+
+ currentListEntry = currentListEntry->Flink;
+ }
+
+ ExReleasePushLock(&RuleSetEntry->RuleListPushLock);
+ KeLeaveCriticalRegion();
+
+ return STATUS_INVALID_PARAMETER_2;
+}
+
+/* KphpSsAddRule
+ *
+ * Adds a rule entry to a ruleset entry.
+ */
+NTSTATUS KphpSsAddRule(
+ __out PKPHSS_RULE_ENTRY *RuleEntry,
+ __in PKPHSS_RULESET_ENTRY RuleSetEntry,
+ __in KPHSS_FILTER_TYPE FilterType,
+ __in KPHSS_RULE_TYPE RuleType
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PKPHSS_RULE_ENTRY ruleEntry;
+
+ /* Make sure the filter/rule type is valid. */
+ if (FilterType < IncludeFilterType || FilterType >= MaxFilterType)
+ return STATUS_INVALID_PARAMETER_3;
+ if (RuleType < ProcessIdRuleType || RuleType >= MaxRuleType)
+ return STATUS_INVALID_PARAMETER_4;
+
+ /* Create the rule entry object. */
+ status = KphCreateObject(
+ &ruleEntry,
+ sizeof(KPHSS_RULE_ENTRY),
+ 0,
+ KphSsRuleEntryType,
+ 0
+ );
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ /* Initialize the object. */
+ ruleEntry->Initialized = FALSE;
+ ruleEntry->FilterType = FilterType;
+ ruleEntry->RuleType = RuleType;
+
+ /* Get a handle for the rule. */
+ ruleEntry->Handle = (HANDLE)(ULONG_PTR)InterlockedExchangeAdd(
+ &RuleSetEntry->NextRuleHandle,
+ KPHSS_RULE_HANDLE_INCREMENT
+ );
+
+ /* Add the rule to the ruleset. */
+ KeEnterCriticalRegion();
+ ExAcquirePushLockExclusive(&RuleSetEntry->RuleListPushLock);
+ InsertTailList(&RuleSetEntry->RuleListHead, &ruleEntry->RuleListEntry);
+ ExReleasePushLock(&RuleSetEntry->RuleListPushLock);
+ KeLeaveCriticalRegion();
+ /* Add a reference for the rule being on the list. */
+ KphReferenceObject(ruleEntry);
+
+ *RuleEntry = ruleEntry;
+
+ return status;
+}
+
+/* KphpSsCreateEventBlock
+ *
+ * Allocates and initializes an event block.
+ *
+ * EventBlock: A variable which receives a pointer to the event block.
+ * Thread: The thread for which the event is being generated.
+ * Number: The system service number.
+ * Arguments: A pointer to the caller-supplied arguments.
+ * NumberOfArguments: The number of arguments, in ULONGs.
+ */
+NTSTATUS KphpSsCreateEventBlock(
+ __out PKPHSS_EVENT_BLOCK *EventBlock,
+ __in PKTHREAD Thread,
+ __in ULONG Number,
+ __in ULONG *Arguments,
+ __in ULONG NumberOfArguments
+ )
+{
+ PKPHSS_EVENT_BLOCK eventBlock;
+ KPROCESSOR_MODE previousMode;
+ ULONG eventBlockSize;
+ ULONG argumentsSize;
+ ULONG traceSize;
+ PVOID stackTrace[MAX_STACK_DEPTH * 2];
+ ULONG capturedFrames;
+
+ /* Make sure the argument count isn't too large. */
+ if (NumberOfArguments > MAX_USHORT)
+ return STATUS_INVALID_PARAMETER;
+
+ previousMode = ExGetPreviousMode();
+
+ /* Capture kernel-mode and user-mode stack traces.
+ * We do this before we allocate the event block so
+ * we can calculate how large the block should be.
+ */
+
+ /* Get a kernel-mode stack trace. */
+ capturedFrames = KphCaptureStackBackTrace(
+ 0,
+ MAX_STACK_DEPTH - 1,
+ 0,
+ stackTrace,
+ NULL
+ );
+
+ if (PsGetCurrentProcess() != PsInitialSystemProcess)
+ {
+ /* Get a user-mode stack trace. */
+ capturedFrames += KphCaptureStackBackTrace(
+ 0,
+ MAX_STACK_DEPTH - 1,
+ RTL_WALK_USER_MODE_STACK,
+ &stackTrace[capturedFrames],
+ NULL
+ );
+ }
+
+ /* Calculate the size of the event block. */
+ argumentsSize = NumberOfArguments * sizeof(ULONG);
+ traceSize = capturedFrames * sizeof(PVOID);
+ eventBlockSize = sizeof(KPHSS_EVENT_BLOCK) + argumentsSize + traceSize;
+
+ /* Make sure the block size isn't too large. */
+ if (eventBlockSize > MAX_USHORT)
+ return STATUS_INVALID_PARAMETER;
+
+ /* Allocate the event block. */
+ eventBlock = ExAllocatePoolWithTag(PagedPool, eventBlockSize, TAG_EVENT_BLOCK);
+
+ if (!eventBlock)
+ return STATUS_INSUFFICIENT_RESOURCES;
+
+ /* Initialize the event block. */
+ eventBlock->Header.Size = (USHORT)eventBlockSize;
+ eventBlock->Header.Type = EventBlockType;
+ eventBlock->Flags = 0;
+ KeQuerySystemTime(&eventBlock->Time);
+ eventBlock->ClientId.UniqueThread = PsGetThreadId(Thread);
+ eventBlock->ClientId.UniqueProcess = PsGetProcessId(IoThreadToProcess(Thread));
+ eventBlock->Number = Number;
+ eventBlock->NumberOfArguments = (USHORT)NumberOfArguments;
+ eventBlock->ArgumentsOffset = sizeof(KPHSS_EVENT_BLOCK);
+ eventBlock->TraceCount = (USHORT)capturedFrames;
+ eventBlock->TraceOffset = (USHORT)(sizeof(KPHSS_EVENT_BLOCK) + argumentsSize);
+
+ /* Set the flags according to the previous mode. */
+ if (previousMode == UserMode)
+ eventBlock->Flags |= KPHSS_EVENT_USER_MODE;
+ else if (previousMode == KernelMode)
+ eventBlock->Flags |= KPHSS_EVENT_KERNEL_MODE;
+
+ /* Probe and copy the arguments. */
+ if (previousMode != KernelMode)
+ {
+ __try
+ {
+ ProbeForRead(Arguments, argumentsSize, 4);
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ eventBlock->Flags |= KPHSS_EVENT_PROBE_ARGUMENTS_FAILED;
+ }
+ }
+
+ __try
+ {
+ /* Copy the arguments to the space immediately after the event block. */
+ memcpy((PCHAR)eventBlock + eventBlock->ArgumentsOffset, Arguments, argumentsSize);
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ eventBlock->Flags |= KPHSS_EVENT_COPY_ARGUMENTS_FAILED;
+ }
+
+ /* Copy the stack trace. */
+ memcpy((PCHAR)eventBlock + eventBlock->TraceOffset, stackTrace, traceSize);
+
+ /* Pass the pointer to the event block back. */
+ *EventBlock = eventBlock;
+
+ return STATUS_SUCCESS;
+}
+
+/* KphpSsFreeEventBlock
+ *
+ * Frees an event block created by KphpSsCreateEventBlock.
+ */
+VOID KphpSsFreeEventBlock(
+ __in PKPHSS_EVENT_BLOCK EventBlock
+ )
+{
+ ExFreePoolWithTag(EventBlock, TAG_EVENT_BLOCK);
+}
+
+/* KphpSsCaptureSimpleArgument
+ *
+ * Captures a simple (1-, 2-, 4- or 8-byte) argument.
+ */
+NTSTATUS KphpSsCaptureSimpleArgument(
+ __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock,
+ __in PVOID Argument,
+ __in KPHSS_ARGUMENT_TYPE Type,
+ __in KPROCESSOR_MODE PreviousMode
+ )
+{
+ PKPHSS_ARGUMENT_BLOCK argumentBlock;
+ ULONG size;
+ LARGE_INTEGER value;
+
+ /* Return if we have a NULL pointer. */
+ if (!Argument)
+ return STATUS_INVALID_PARAMETER_2;
+
+ /* Get the proper argument size based on the argument type. */
+ switch (Type)
+ {
+ case Int8Argument:
+ size = sizeof(BOOLEAN);
+ break;
+ case Int16Argument:
+ size = sizeof(SHORT);
+ break;
+ case Int32Argument:
+ size = sizeof(LONG);
+ break;
+ case Int64Argument:
+ size = sizeof(LARGE_INTEGER);
+ break;
+ default:
+ return STATUS_INVALID_PARAMETER_3;
+ }
+
+ /* Probe and read the value. */
+ __try
+ {
+ if (PreviousMode != KernelMode)
+ ProbeForRead(Argument, size, 1);
+
+ memcpy(&value, Argument, size);
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ return GetExceptionCode();
+ }
+
+ /* Allocate an argument block. */
+ argumentBlock = KphpSsAllocateArgumentBlock(size, Type);
+
+ if (!argumentBlock)
+ return STATUS_INSUFFICIENT_RESOURCES;
+
+ /* Copy the value into the argument block. */
+ memcpy(&argumentBlock->Simple, &value, size);
+ *ArgumentBlock = argumentBlock;
+
+ return STATUS_SUCCESS;
+}
+
+/* KphpSsCaptureHandleArgument
+ *
+ * Captures a handle argument.
+ */
+NTSTATUS KphpSsCaptureHandleArgument(
+ __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock,
+ __in HANDLE Argument,
+ __in KPROCESSOR_MODE PreviousMode
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PKPHSS_ARGUMENT_BLOCK argumentBlock;
+ ULONG bufferLength;
+ PVOID object;
+ POBJECT_TYPE objectType;
+ PUNICODE_STRING objectTypeName;
+ PUNICODE_STRING objectNameInfo;
+ ULONG returnLength;
+ PKPHSS_HANDLE handleInfo;
+ PKPHSS_WSTRING wString;
+
+ /* Return if we have a NULL handle. */
+ if (!Argument)
+ return STATUS_INVALID_PARAMETER_2;
+
+ /* Make sure the handle isn't a kernel handle if we're
+ * from user-mode. We need exceptions for the process
+ * and thread pseudo-handles.
+ */
+ if (PreviousMode != KernelMode)
+ {
+ if (
+ IsKernelHandle(Argument) &&
+ Argument != NtCurrentProcess() &&
+ Argument != NtCurrentThread()
+ )
+ return STATUS_INVALID_HANDLE;
+ }
+
+ /* Reference the object. */
+ status = ObReferenceObjectByHandle(
+ Argument,
+ 0,
+ NULL,
+ KernelMode,
+ &object,
+ NULL
+ );
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ /* Get a pointer to the UNICODE_STRING containing the
+ * object type name.
+ */
+ objectType = KphGetObjectTypeNt(object);
+ objectTypeName = (PUNICODE_STRING)KVOFF(objectType, OffOtName);
+
+ /* Allocate a buffer for name information. */
+ objectNameInfo = (PUNICODE_STRING)ExAllocatePoolWithTag(
+ PagedPool,
+ CAPTURE_HANDLE_BUFFER_SIZE,
+ TAG_CAPTURE_TEMP_BUFFER
+ );
+
+ if (!objectNameInfo)
+ goto CleanupObject;
+
+ /* Query the name of the object. */
+ status = KphQueryNameObject(
+ object,
+ objectNameInfo,
+ CAPTURE_HANDLE_BUFFER_SIZE,
+ &returnLength
+ );
+
+ if (!NT_SUCCESS(status))
+ goto CleanupName;
+
+ /* Allocate an argument block. */
+ argumentBlock = KphpSsAllocateArgumentBlock(
+ sizeof(KPHSS_HANDLE) + sizeof(KPHSS_WSTRING) + sizeof(KPHSS_WSTRING) +
+ objectTypeName->Length + objectNameInfo->Length,
+ HandleArgument
+ );
+
+ if (!argumentBlock)
+ goto CleanupName;
+
+ handleInfo = &argumentBlock->Handle;
+ /* Calculate the offsets. */
+ handleInfo->TypeNameOffset = sizeof(KPHSS_HANDLE);
+ handleInfo->NameOffset =
+ handleInfo->TypeNameOffset + sizeof(KPHSS_WSTRING) +
+ objectTypeName->Length;
+
+ /* Copy the object type name into the block. */
+ wString = (PKPHSS_WSTRING)PTR_ADD_OFFSET(handleInfo, handleInfo->TypeNameOffset);
+ wString->Length = objectTypeName->Length;
+ memcpy(&wString->Buffer, objectTypeName->Buffer, wString->Length);
+
+ /* Copy the object name into the block. */
+ wString = (PKPHSS_WSTRING)PTR_ADD_OFFSET(handleInfo, handleInfo->NameOffset);
+ wString->Length = objectNameInfo->Length;
+ memcpy(&wString->Buffer, objectNameInfo->Buffer, wString->Length);
+
+ /* We may be able to get additional information for the
+ * object.
+ */
+
+ handleInfo->ClientId.UniqueProcess = NULL;
+ handleInfo->ClientId.UniqueThread = NULL;
+
+ if (objectType == *PsProcessType)
+ {
+ handleInfo->ClientId.UniqueProcess = PsGetProcessId((PEPROCESS)object);
+ }
+ else if (objectType == *PsThreadType)
+ {
+ handleInfo->ClientId.UniqueThread = PsGetThreadId((PETHREAD)object);
+ handleInfo->ClientId.UniqueProcess = PsGetProcessId(IoThreadToProcess((PETHREAD)object));
+ }
+
+ *ArgumentBlock = argumentBlock;
+
+CleanupName:
+ ExFreePoolWithTag(objectNameInfo, TAG_CAPTURE_TEMP_BUFFER);
+CleanupObject:
+ ObDereferenceObject(object);
+
+ return status;
+}
+
+/* KphpSsCaptureUnicodeStringArgument
+ *
+ * Captures a UNICODE_STRING argument.
+ */
+NTSTATUS KphpSsCaptureUnicodeStringArgument(
+ __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock,
+ __in PUNICODE_STRING Argument,
+ __in KPROCESSOR_MODE PreviousMode
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PKPHSS_ARGUMENT_BLOCK argumentBlock;
+ UNICODE_STRING unicodeString;
+
+ /* Return if we have a NULL pointer. */
+ if (!Argument)
+ return STATUS_INVALID_PARAMETER_2;
+
+ /* Probe and copy the UNICODE_STRING structure. */
+ __try
+ {
+ if (PreviousMode != KernelMode)
+ ProbeForRead(Argument, sizeof(UNICODE_STRING), 1);
+
+ memcpy(&unicodeString, Argument, sizeof(UNICODE_STRING));
+
+ /* Probe the buffer, if present. */
+ if (unicodeString.Buffer && PreviousMode != KernelMode)
+ {
+ ProbeForRead(unicodeString.Buffer, unicodeString.Length, 1);
+ }
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ return GetExceptionCode();
+ }
+
+ /* Check if the string is too large. */
+ if (unicodeString.Length > CAPTURE_UNICODE_STRING_MAX_SIZE)
+ return STATUS_UNSUCCESSFUL;
+
+ /* Allocate an argument block. */
+ argumentBlock = KphpSsAllocateArgumentBlock(
+ sizeof(KPHSS_UNICODE_STRING) + unicodeString.Length,
+ UnicodeStringArgument
+ );
+
+ if (!argumentBlock)
+ return STATUS_INSUFFICIENT_RESOURCES;
+
+ /* Copy the string into the argument block. */
+ argumentBlock->UnicodeString.Length = unicodeString.Length;
+ argumentBlock->UnicodeString.MaximumLength = unicodeString.MaximumLength;
+ argumentBlock->UnicodeString.Pointer = unicodeString.Buffer;
+
+ if (unicodeString.Buffer)
+ {
+ __try
+ {
+ memcpy(argumentBlock->UnicodeString.Buffer, unicodeString.Buffer, unicodeString.Length);
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ KphpSsFreeArgumentBlock(argumentBlock);
+ return GetExceptionCode();
+ }
+ }
+
+ *ArgumentBlock = argumentBlock;
+
+ return status;
+}
+
+/* KphpSsCaptureObjectAttributesArgument
+ *
+ * Captures an OBJECT_ATTRIBUTES argument.
+ */
+NTSTATUS KphpSsCaptureObjectAttributesArgument(
+ __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock,
+ __in POBJECT_ATTRIBUTES Argument,
+ __in KPROCESSOR_MODE PreviousMode
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PKPHSS_ARGUMENT_BLOCK argumentBlock;
+ OBJECT_ATTRIBUTES objectAttributes;
+ PKPHSS_ARGUMENT_BLOCK rootDirectoryArgumentBlock = NULL;
+ ULONG rootDirectoryArgumentBlockSize = 0;
+ PKPHSS_ARGUMENT_BLOCK objectNameArgumentBlock = NULL;
+ ULONG objectNameArgumentBlockSize = 0;
+
+ /* Return if we have a NULL pointer. */
+ if (!Argument)
+ return STATUS_INVALID_PARAMETER_2;
+
+ /* Probe and copy the OBJECT_ATTRIBUTES structure. */
+ __try
+ {
+ if (PreviousMode != KernelMode)
+ ProbeForRead(Argument, sizeof(OBJECT_ATTRIBUTES), 1);
+
+ memcpy(&objectAttributes, Argument, sizeof(OBJECT_ATTRIBUTES));
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ return GetExceptionCode();
+ }
+
+ /* If we have a root directory, create an argument block from it
+ * and copy it to our argument block.
+ */
+ if (objectAttributes.RootDirectory)
+ {
+ status = KphpSsCaptureHandleArgument(
+ &rootDirectoryArgumentBlock,
+ objectAttributes.RootDirectory,
+ PreviousMode
+ );
+
+ /* If we created the argument block, we need to calculate
+ * the size of the KPHSS_HANDLE structure.
+ */
+ if (NT_SUCCESS(status))
+ {
+ rootDirectoryArgumentBlockSize =
+ rootDirectoryArgumentBlock->Header.Size - KPHSS_ARGUMENT_BLOCK_OVERHEAD;
+ }
+ else
+ {
+ rootDirectoryArgumentBlock = NULL;
+ }
+ }
+
+ /* If we have a object name, create an argument block from it and
+ * copy it to our argument block.
+ */
+ if (objectAttributes.ObjectName)
+ {
+ status = KphpSsCaptureUnicodeStringArgument(
+ &objectNameArgumentBlock,
+ objectAttributes.ObjectName,
+ PreviousMode
+ );
+
+ /* If we created the argument block, we need to calculate
+ * the size of the KPHSS_UNICODE_STRING structure.
+ */
+ if (NT_SUCCESS(status))
+ {
+ objectNameArgumentBlockSize =
+ objectNameArgumentBlock->Header.Size - KPHSS_ARGUMENT_BLOCK_OVERHEAD;
+ }
+ else
+ {
+ objectNameArgumentBlock = NULL;
+ }
+ }
+
+ /* Allocate an argument block. */
+ argumentBlock = KphpSsAllocateArgumentBlock(
+ sizeof(KPHSS_OBJECT_ATTRIBUTES) + rootDirectoryArgumentBlockSize + objectNameArgumentBlockSize,
+ ObjectAttributesArgument
+ );
+
+ argumentBlock->ObjectAttributes.RootDirectoryOffset = 0;
+ argumentBlock->ObjectAttributes.ObjectNameOffset = 0;
+
+ /* Copy the object attributes fields. */
+ memcpy(
+ &argumentBlock->ObjectAttributes.ObjectAttributes,
+ &objectAttributes,
+ sizeof(OBJECT_ATTRIBUTES)
+ );
+
+ /* Copy the root directory structure, if we have one. */
+ if (rootDirectoryArgumentBlock)
+ {
+ ULONG rootDirectoryOffset;
+
+ /* It will go directly after the KPHSS_OBJECT_ATTRIBUTES structure. */
+ rootDirectoryOffset = sizeof(KPHSS_OBJECT_ATTRIBUTES);
+ argumentBlock->ObjectAttributes.RootDirectoryOffset = (USHORT)rootDirectoryOffset;
+ /* Copy it. */
+ memcpy(
+ PTR_ADD_OFFSET(&argumentBlock->ObjectAttributes, rootDirectoryOffset),
+ &rootDirectoryArgumentBlock->Handle,
+ rootDirectoryArgumentBlockSize
+ );
+ /* Free the block. */
+ KphpSsFreeArgumentBlock(rootDirectoryArgumentBlock);
+ }
+
+ /* Copy the object name structure, if we have one. */
+ if (objectNameArgumentBlock)
+ {
+ ULONG objectNameOffset;
+
+ /* We'll place the structure after the root directory structure,
+ * if present.
+ */
+ objectNameOffset = sizeof(KPHSS_OBJECT_ATTRIBUTES) + rootDirectoryArgumentBlockSize;
+
+ /* Make sure the offset isn't too large. */
+ if (objectNameOffset <= MAX_USHORT)
+ {
+ argumentBlock->ObjectAttributes.ObjectNameOffset = (USHORT)objectNameOffset;
+ /* Copy it. */
+ memcpy(
+ PTR_ADD_OFFSET(&argumentBlock->ObjectAttributes, objectNameOffset),
+ &objectNameArgumentBlock->UnicodeString,
+ objectNameArgumentBlockSize
+ );
+ }
+
+ /* Free the block. */
+ KphpSsFreeArgumentBlock(objectNameArgumentBlock);
+ }
+
+ *ArgumentBlock = argumentBlock;
+
+ return status;
+}
+
+/* KphpSsCaptureClientIdArgument
+ *
+ * Captures a CLIENT_ID argument.
+ */
+NTSTATUS KphpSsCaptureClientIdArgument(
+ __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock,
+ __in PCLIENT_ID Argument,
+ __in KPROCESSOR_MODE PreviousMode
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ CLIENT_ID clientId;
+ PKPHSS_ARGUMENT_BLOCK argumentBlock;
+
+ /* Check if we have a NULL pointer. */
+ if (!Argument)
+ return STATUS_INVALID_PARAMETER_2;
+
+ /* Probe and copy the CLIENT_ID structure. */
+ __try
+ {
+ if (PreviousMode != KernelMode)
+ ProbeForRead(Argument, sizeof(CLIENT_ID), 1);
+
+ memcpy(&clientId, Argument, sizeof(CLIENT_ID));
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ return GetExceptionCode();
+ }
+
+ /* Allocate an argument block. */
+ argumentBlock = KphpSsAllocateArgumentBlock(
+ sizeof(CLIENT_ID),
+ ClientIdArgument
+ );
+
+ if (!argumentBlock)
+ return STATUS_INSUFFICIENT_RESOURCES;
+
+ /* Fill in the argument block. */
+ memcpy(&argumentBlock->ClientId, &clientId, sizeof(CLIENT_ID));
+
+ *ArgumentBlock = argumentBlock;
+
+ return status;
+}
+
+/* KphpSsCreateArgumentBlock
+ *
+ * Allocates and initializes an argument block.
+ */
+NTSTATUS KphpSsCreateArgumentBlock(
+ __out PKPHSS_ARGUMENT_BLOCK *ArgumentBlock,
+ __in ULONG Number,
+ __in ULONG Argument,
+ __in ULONG Index
+ )
+{
+#ifdef _X86_
+ NTSTATUS status = STATUS_SUCCESS;
+ PKPHSS_ARGUMENT_BLOCK argumentBlock;
+ KPROCESSOR_MODE previousMode;
+ PKPHSS_CALL_ENTRY callEntry;
+ KPHSS_ARGUMENT_TYPE argumentType;
+
+ previousMode = ExGetPreviousMode();
+
+ /* Get a pointer to the call entry for the system service.
+ * If we don't have one, we can't proceed.
+ */
+ callEntry = KphSsLookupCallEntry(Number);
+
+ if (!callEntry)
+ return STATUS_INVALID_PARAMETER_2;
+
+ /* Validate the argument index. */
+ if (Index >= callEntry->NumberOfArguments)
+ return STATUS_INVALID_PARAMETER_3;
+
+ /* Is this a normal argument? If so, there's no point
+ * creating an argument block since the data is already
+ * in the event block.
+ */
+ argumentType = callEntry->Arguments[Index];
+
+ if (argumentType == NormalArgument)
+ return STATUS_UNSUCCESSFUL;
+
+ /* Capture the argument. */
+
+ switch (argumentType)
+ {
+ case Int8Argument:
+ case Int16Argument:
+ case Int32Argument:
+ case Int64Argument:
+ status = KphpSsCaptureSimpleArgument(
+ &argumentBlock,
+ (PVOID)Argument,
+ argumentType,
+ previousMode
+ );
+ break;
+ case HandleArgument:
+ status = KphpSsCaptureHandleArgument(
+ &argumentBlock,
+ (HANDLE)Argument,
+ previousMode
+ );
+ break;
+ case UnicodeStringArgument:
+ status = KphpSsCaptureUnicodeStringArgument(
+ &argumentBlock,
+ (PUNICODE_STRING)Argument,
+ previousMode
+ );
+ break;
+ case ObjectAttributesArgument:
+ status = KphpSsCaptureObjectAttributesArgument(
+ &argumentBlock,
+ (POBJECT_ATTRIBUTES)Argument,
+ previousMode
+ );
+ break;
+ case ClientIdArgument:
+ status = KphpSsCaptureClientIdArgument(
+ &argumentBlock,
+ (PCLIENT_ID)Argument,
+ previousMode
+ );
+ break;
+ default:
+ status = STATUS_NOT_IMPLEMENTED;
+ break;
+ }
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ /* Put the index in. */
+ argumentBlock->Index = (UCHAR)Index;
+
+ *ArgumentBlock = argumentBlock;
+
+ return status;
+#else
+ return STATUS_NOT_SUPPORTED;
+#endif
+}
+
+/* KphpSsAllocateArgumentBlock
+ *
+ * Allocates an argument block and initializes some fields.
+ */
+PKPHSS_ARGUMENT_BLOCK KphpSsAllocateArgumentBlock(
+ __in ULONG InnerSize,
+ __in KPHSS_ARGUMENT_TYPE Type
+ )
+{
+ PKPHSS_ARGUMENT_BLOCK argumentBlock;
+ ULONG size;
+
+ size = KPHSS_ARGUMENT_BLOCK_SIZE(InnerSize);
+
+ /* Make sure the size isn't too large. */
+ if (size > MAX_USHORT)
+ return NULL;
+
+ argumentBlock = ExAllocatePoolWithTag(
+ PagedPool,
+ size,
+ TAG_ARGUMENT_BLOCK
+ );
+
+ if (!argumentBlock)
+ return NULL;
+
+ argumentBlock->Header.Type = ArgumentBlockType;
+ argumentBlock->Header.Size = (USHORT)size;
+ argumentBlock->Type = Type;
+
+ return argumentBlock;
+}
+
+/* KphpSsFreeArgumentBlock
+ *
+ * Frees an argument block created by KphpSsCreateArgumentBlock.
+ */
+VOID KphpSsFreeArgumentBlock(
+ __in PKPHSS_ARGUMENT_BLOCK ArgumentBlock
+ )
+{
+ ExFreePoolWithTag(ArgumentBlock, TAG_ARGUMENT_BLOCK);
+}
+
+/* KphpSsWriteBlock
+ *
+ * Writes a block into client memory.
+ */
+NTSTATUS KphpSsWriteBlock(
+ __in PKPHSS_CLIENT_ENTRY ClientEntry,
+ __in_opt PKPHSS_BLOCK_HEADER Block,
+ __in KPHSS_SEQUENCE_MODE SequenceMode
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ LARGE_INTEGER zeroTimeout;
+ KPH_ATTACH_STATE attachState;
+ ULONG availableSpace;
+ HANDLE dupHandleInClient = NULL;
+
+ zeroTimeout.QuadPart = 0;
+
+ /* Take care of the sequence mode. If it isn't
+ * NoSequence, it is effectively a way for the caller
+ * to control the buffer mutex.
+ */
+ if (SequenceMode == StartSequence)
+ {
+ ExAcquireFastMutex(&ClientEntry->BufferMutex);
+ return STATUS_SUCCESS;
+ }
+ else if (SequenceMode == EndSequence)
+ {
+ ExReleaseFastMutex(&ClientEntry->BufferMutex);
+ return STATUS_SUCCESS;
+ }
+ else
+ {
+ /* If we aren't manipulating the mutex, we need
+ * a block to write.
+ */
+ if (!Block)
+ return STATUS_INVALID_PARAMETER_2;
+
+ /* If we're in a sequence, don't acquire the mutex
+ * because the caller would have acquired it using
+ * StartSequence already.
+ */
+ if (SequenceMode != InSequence)
+ ExAcquireFastMutex(&ClientEntry->BufferMutex);
+ }
+
+ /* Try to acquire the write semaphore. If we can't acquire
+ * it immediately, drop the block.
+ */
+ status = KeWaitForSingleObject(
+ ClientEntry->WriteSemaphore,
+ Executive,
+ KernelMode,
+ FALSE,
+ &zeroTimeout
+ );
+
+ if (!KPHSS_BLOCK_SUCCESS(status))
+ {
+ if (status == STATUS_TIMEOUT)
+ {
+ dprintf("Ss: WARNING: Dropped block (server %#x).\n", ClientEntry->BufferCursor);
+ ClientEntry->NumberOfBlocksDropped++;
+ }
+
+ goto CleanupBufferMutex;
+ }
+
+ availableSpace = ClientEntry->BufferSize - ClientEntry->BufferCursor;
+
+ /* Blocks are recorded in a circular buffer.
+ * In the case that there is not enough space for an entire block,
+ * we will record a reset block that tells the client to reset
+ * its read cursor to 0. In the case that there is not enough
+ * space for a block header, it is implied that the client will
+ * reset its read cursor.
+ */
+
+ /* Check if we have enough space for a block header. */
+ if (availableSpace < sizeof(KPHSS_BLOCK_HEADER))
+ {
+ /* Not enough space. Reset the cursor. */
+ dprintf("Ss: Implicit cursor reset (server %#x).\n", ClientEntry->BufferCursor);
+ ClientEntry->BufferCursor = 0;
+ availableSpace = ClientEntry->BufferSize;
+ }
+ /* Check if we have enough space for the block. */
+ else if (availableSpace < Block->Size)
+ {
+ KPHSS_RESET_BLOCK resetBlock;
+
+ /* Not enough space for the block, but enough space
+ * for a reset block. Write the reset block and reset
+ * the cursor.
+ */
+ resetBlock.Header.Size = sizeof(KPHSS_RESET_BLOCK);
+ resetBlock.Header.Type = ResetBlockType;
+
+ /* Attach to the client process and copy the block. */
+ KphAttachProcess(ClientEntry->Process, &attachState);
+
+ __try
+ {
+ memcpy(
+ PTR_ADD_OFFSET(ClientEntry->BufferBase, ClientEntry->BufferCursor),
+ &resetBlock,
+ resetBlock.Header.Size
+ );
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ KphDetachProcess(&attachState);
+ status = GetExceptionCode();
+ goto CleanupBufferMutex;
+ }
+
+ dprintf("Ss: Wrote reset block (server %#x).\n", ClientEntry->BufferCursor);
+ KphDetachProcess(&attachState);
+ ClientEntry->BufferCursor = 0;
+ availableSpace = ClientEntry->BufferSize;
+ }
+
+ /* Now that we have dealt with any end-of-buffer issues,
+ * we still have to check if we have enough space for the
+ * event. We may have a huge event or the client may have a
+ * tiny buffer.
+ */
+ if (availableSpace < Block->Size)
+ {
+ dfprintf("Ss: WARNING: Insufficient buffer size (server %#x).\n", ClientEntry->BufferCursor);
+ status = STATUS_BUFFER_TOO_SMALL;
+ goto CleanupBufferMutex;
+ }
+
+ /* Time to copy the block into the buffer.
+ */
+ KphAttachProcess(ClientEntry->Process, &attachState);
+
+ __try
+ {
+ memcpy(
+ PTR_ADD_OFFSET(ClientEntry->BufferBase, ClientEntry->BufferCursor),
+ Block,
+ Block->Size
+ );
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ dfprintf("Ss: ERROR: Could not write to the client buffer (server %#x)!\n", ClientEntry->BufferCursor);
+ KphDetachProcess(&attachState);
+ status = GetExceptionCode();
+ goto CleanupBufferMutex;
+ }
+
+ KphDetachProcess(&attachState);
+
+ /* Now that we have succesfully copied the block, we need to
+ * release the read semaphore to notify to the client that they have
+ * a block to read. We also need to advance our cursor.
+ */
+
+ /* May cause an exception (STATUS_SEMAPHORE_LIMIT_EXCEEDED). */
+ __try
+ {
+ KeReleaseSemaphore(ClientEntry->ReadSemaphore, 2, 1, FALSE);
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ dfprintf("Ss: ERROR: Could not release read semaphore (server %#x)!\n", ClientEntry->BufferCursor);
+ status = GetExceptionCode();
+ goto CleanupBufferMutex;
+ }
+
+ ClientEntry->BufferCursor += Block->Size;
+ ClientEntry->NumberOfBlocksWritten++;
+
+ dprintf("Ss: Wrote block (server %#x).\n", ClientEntry->BufferCursor);
+
+CleanupBufferMutex:
+ if (SequenceMode != InSequence)
+ ExReleaseFastMutex(&ClientEntry->BufferMutex);
+
+ return status;
+}
+
+/* KphpSsLogSystemServiceCall
+ *
+ * Logs a system service.
+ *
+ * WARNING: This function CANNOT make any system calls.
+ *
+ * IRQL: <= APC_LEVEL
+ */
+VOID NTAPI KphpSsLogSystemServiceCall(
+ __in ULONG Number,
+ __in ULONG *Arguments,
+ __in ULONG NumberOfArguments,
+ __in PKSERVICE_TABLE_DESCRIPTOR ServiceTable,
+ __in PKTHREAD Thread
+ )
+{
+#ifdef _X86_
+ NTSTATUS status = STATUS_SUCCESS;
+ KPROCESSOR_MODE previousMode;
+ PLIST_ENTRY currentListEntry;
+ PKPHSS_RULESET_ENTRY ruleSetEntryArray[KPHSS_RULESET_ENTRY_LIMIT];
+ ULONG ruleSetEntryCount;
+ PKPHSS_EVENT_BLOCK eventBlock;
+ PKPHSS_ARGUMENT_BLOCK argumentBlockArray[KPHSS_MAXIMUM_ARGUMENT_BLOCKS];
+ ULONG i, j;
+
+ previousMode = ExGetPreviousMode();
+ /* Ignore the Thread argument. Replace it with our own. */
+ Thread = KeGetCurrentThread();
+
+ /* First, some checks.
+ * * We can't operate at IRQL > APC_LEVEL because
+ * of restrictions on logging.
+ * * We can't operate on unknown service tables like the
+ * shadow service table (yet).
+ * * We have to make sure we aren't attempting to log
+ * a call to ZwContinue because we caused an exception
+ * last time we were logging something. This will cause
+ * a deadlock!
+ */
+
+ if (KeGetCurrentIrql() > APC_LEVEL)
+ return;
+ if (
+ ServiceTable->Base != __KeServiceDescriptorTable->Base ||
+ ServiceTable->Number != __KeServiceDescriptorTable->Number ||
+ ServiceTable->Limit != __KeServiceDescriptorTable->Limit
+ )
+ return;
+
+ /* Make sure we aren't logging ZwContinue if it's because
+ * we caused an exception somewhere. */
+ if (
+ ServiceTable->Base == __KeServiceDescriptorTable->Base &&
+ Number == SsNtContinue &&
+ NumberOfArguments == 2 &&
+ previousMode == KernelMode
+ )
+ {
+ /* "Reverse probe" the arguments. */
+ if (
+ (ULONG_PTR)Arguments > (ULONG_PTR)MmHighestUserAddress &&
+ Arguments[0] > (ULONG_PTR)MmHighestUserAddress
+ )
+ {
+ CONTEXT context;
+
+ /* The first argument contains the context. */
+ memcpy(&context, (PCONTEXT)Arguments[0], sizeof(CONTEXT));
+ /* Check if the context Eip points into the KPH module.
+ * If so, abort the logging.
+ */
+ if (
+ context.Eip >= (ULONG_PTR)KphDriverObject->DriverStart &&
+ context.Eip < (ULONG_PTR)KphDriverObject->DriverStart + KphDriverObject->DriverSize
+ )
+ return;
+ }
+ }
+
+ /* Build the ruleset entry array by going through the ruleset
+ * list, referencing each relevant one and copying them into
+ * the local array. This we way don't hold the lock for too
+ * long.
+ */
+
+ KeEnterCriticalRegion();
+ ExAcquirePushLockShared(&KphSsRuleSetListPushLock);
+
+ currentListEntry = KphSsRuleSetListHead.Flink;
+ ruleSetEntryCount = 0;
+
+ while (
+ currentListEntry != &KphSsRuleSetListHead &&
+ ruleSetEntryCount < KPHSS_RULESET_ENTRY_LIMIT
+ )
+ {
+ PKPHSS_RULESET_ENTRY ruleSetEntry = KPHSS_RULESET_ENTRY(currentListEntry);
+
+ if (KphpSsMatchRuleSetEntry(
+ ruleSetEntry,
+ Number,
+ Arguments,
+ NumberOfArguments,
+ ServiceTable,
+ Thread,
+ previousMode
+ ))
+ {
+ /* Reference and store the ruleset entry in the local array. */
+ if (KphReferenceObjectSafe(ruleSetEntry))
+ {
+ /* Make sure the client is enabled. */
+ if (ruleSetEntry->Client->Enabled)
+ {
+ ruleSetEntryArray[ruleSetEntryCount] = ruleSetEntry;
+ ruleSetEntryCount++;
+ }
+ else
+ {
+ /* We need to use defer delete here because we hold the
+ * ruleset list lock.
+ */
+ KphDereferenceObjectDeferDelete(ruleSetEntry);
+ }
+ }
+ }
+
+ currentListEntry = currentListEntry->Flink;
+ }
+
+ ExReleasePushLock(&KphSsRuleSetListPushLock);
+ KeLeaveCriticalRegion();
+
+ /* If we didn't find any ruleset entries, don't bother creating the
+ * event block.
+ */
+ if (ruleSetEntryCount == 0)
+ return;
+
+ /* We have work to do. Create an event block first. */
+ if (!NT_SUCCESS(KphpSsCreateEventBlock(
+ &eventBlock,
+ Thread,
+ Number,
+ Arguments,
+ NumberOfArguments
+ )))
+ {
+ dfprintf("Ss: ERROR: Unable to create an event block!\n");
+ return;
+ }
+
+ /* Create the argument blocks. If we fail to create one,
+ * set the array entry to NULL and we'll skip it later.
+ */
+
+ for (i = 0; i < NumberOfArguments && i < KPHSS_MAXIMUM_ARGUMENT_BLOCKS; i++)
+ {
+ ULONG argument;
+
+ __try
+ {
+ /* We'll assume the arguments have already been probed
+ * since we created the event block successfully.
+ */
+ argument = Arguments[i];
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ /* Silently skip this argument. Even though it is 99% likely
+ * that we will fail to read the next argument, continue
+ * anyway.
+ */
+ argumentBlockArray[i] = NULL;
+ continue;
+ }
+
+ status = KphpSsCreateArgumentBlock(
+ &argumentBlockArray[i],
+ Number,
+ argument,
+ i
+ );
+
+ if (!NT_SUCCESS(status))
+ argumentBlockArray[i] = NULL;
+ }
+
+ /* Go through the ruleset entry array and write the blocks to each
+ * client. While we're doing that we can also dereference each
+ * ruleset entry.
+ */
+ for (i = 0; i < ruleSetEntryCount; i++)
+ {
+ /* Begin a sequence. */
+ status = KphpSsWriteBlock(ruleSetEntryArray[i]->Client, NULL, StartSequence);
+
+ if (NT_SUCCESS(status))
+ {
+ /* Write the event block. */
+ KphpSsWriteBlock(ruleSetEntryArray[i]->Client, &eventBlock->Header, InSequence);
+
+ /* Write the argument blocks. */
+ for (j = 0; j < NumberOfArguments && j < KPHSS_MAXIMUM_ARGUMENT_BLOCKS; j++)
+ {
+ if (argumentBlockArray[j])
+ {
+ KphpSsWriteBlock(ruleSetEntryArray[i]->Client, &argumentBlockArray[j]->Header, InSequence);
+ }
+ }
+
+ /* End the sequence. */
+ KphpSsWriteBlock(ruleSetEntryArray[i]->Client, NULL, EndSequence);
+ }
+
+ KphDereferenceObject(ruleSetEntryArray[i]);
+ }
+
+ /* Free the event block. */
+ KphpSsFreeEventBlock(eventBlock);
+
+ /* Free the argument blocks. */
+ for (i = 0; i < NumberOfArguments && i < KPHSS_MAXIMUM_ARGUMENT_BLOCKS; i++)
+ {
+ if (argumentBlockArray[i])
+ KphpSsFreeArgumentBlock(argumentBlockArray[i]);
+ }
+#else
+ KeBugCheck(STATUS_NOT_SUPPORTED);
+#endif
+}
+
+#ifdef _X86_
+
+/* KphpSsNewKiFastCallEntry
+ *
+ * The hook function called from within the hooked KiFastCallEntry.
+ */
+__declspec(naked) VOID NTAPI KphpSsNewKiFastCallEntry()
+{
+ /* KiFastCallEntry handles system service calls. User-mode applications
+ * will perform system calls like this:
+ *
+ * Nt*:
+ * mov eax, SystemServiceNumber
+ * mov edx, 0x7ffe0300 <-- at 0x7ffe0300 we have a pointer to KiFastSystemCall
+ * call [edx]
+ * ret
+ *
+ * At KiFastSystemCall:
+ * mov edx, esp
+ * sysenter
+ */
+ /* This means that in KiFastCallEntry, eax will contain the system service
+ * number while edx will contain a pointer to the arguments for the system
+ * service. KiFastCallEntry will fill in edi with the service table, and
+ * esi will contain the caller KTHREAD.
+ *
+ * We cannot hook KiFastCallEntry from the beginning because it starts on the DPC
+ * stack. KiFastCallEntry switches to the proper thread stack, and we want to
+ * hook it just after it switches to the stack. That way we can avoid having to
+ * manually switch the thread stack ourselves.
+ *
+ * At this point:
+ * * eax contains the system service number.
+ * * edx contains a pointer to the user-supplied arguments for
+ * the system service.
+ * * edi contains a pointer to the service table associated with
+ * the system service number.
+ * * esi contains a pointer to the KTHREAD of the caller.
+ */
+ /* Some context:
+ *
+ * push edx
+ * push eax
+ * call [_KeGdiFlushUserBatch]
+ * pop eax
+ * pop edx
+ * inc dword ptr fs:[PbSystemCalls] <-- this gets overwritten with a jmp to here
+ * mov edi, edx
+ * mov ebx, [edi+...]
+ * ...
+ */
+ __asm
+ {
+ /* Save all registers first. */
+ push ebp
+ push edi
+ push esi
+ push edx
+ push ecx
+ push ebx
+ push eax
+
+ /* Since we overwrite the inc instruction when we did the hook,
+ * perform the job now - we have to increment the system calls
+ * counter.
+ */
+ lea ebx, KphSsKiFastCallEntryHook /* get a pointer to the hook structure */
+ mov ebx, dword ptr [ebx+KPH_HOOK.Bytes+3] /* get the PbSystemCalls offset from the original inc instruction */
+ inc dword ptr fs:[ebx] /* increment PbSystemCalls in the PRCB */
+
+ /* Get the number of arguments for this system service. */
+ mov ebx, dword ptr [edi+KSERVICE_TABLE_DESCRIPTOR.Number] /* ebx = a pointer to the argument table */
+ xor ecx, ecx
+ mov cl, [ebx+eax] /* ecx = size of the arguments, in bytes. */
+ shr ecx, 2 /* divide by 2 to get the number of arguments (all ULONGs) */
+
+ /* Call the KiFastCallEntry proc while maintaining the logger count
+ * so that the driver doesn't get unloaded while we're executing.
+ */
+ push esi /* Thread */
+ push edi /* ServiceTable */
+ push ecx /* NumberOfArguments */
+ push edx /* Arguments */
+ push eax /* Number */
+ lock inc dword ptr KphSsNumberOfActiveLoggers
+ call KphpSsLogSystemServiceCall
+ lock dec dword ptr KphSsNumberOfActiveLoggers
+
+ /* Restore the registers and resume execution in KiFastCallEntry. */
+ pop eax
+ pop ebx
+ pop ecx
+ pop edx
+ pop esi
+ pop edi
+ pop ebp
+
+ /* Luckily, KiFastCallEntry will overwrite ebx when we jump back, so it's safe to use it. */
+ lea ebx, __KiFastCallEntry
+ mov ebx, [ebx] /* ebx = KiFastCallEntry at the inc instruction */
+ add ebx, 7 /* skip the inc instruction */
+ jmp ebx /* jump back */
+ }
+}
+
+#else
+
+VOID NTAPI KphpSsNewKiFastCallEntry()
+{
+ KeBugCheck(STATUS_NOT_SUPPORTED);
+}
+
+#endif
diff --git a/branches/ph-plugins/KProcessHacker/sysservicedata.c b/branches/ph-plugins/KProcessHacker/sysservicedata.c
new file mode 100644
index 000000000..c30c36917
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/sysservicedata.c
@@ -0,0 +1,513 @@
+/*
+ * Process Hacker Driver -
+ * system service logging (data)
+ *
+ * Copyright (C) 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 .
+ */
+
+#define _SYSSERVICEDATA_PRIVATE
+#include "include/sysservicedata.h"
+
+PVOID KphpSsCallEntryAllocateRoutine(
+ __in PRTL_GENERIC_TABLE Table,
+ __in CLONG ByteSize
+ );
+
+RTL_GENERIC_COMPARE_RESULTS KphpSsCallEntryCompareRoutine(
+ __in PRTL_GENERIC_TABLE Table,
+ __in PVOID FirstStruct,
+ __in PVOID SecondStruct
+ );
+
+VOID KphpSsCallEntryFreeRoutine(
+ __in PRTL_GENERIC_TABLE Table,
+ __in PVOID Buffer
+ );
+
+KPHSS_CALL_ENTRY SsEntries[] =
+{
+ /* NTSTATUS NtAddAtom(PWSTR String, ULONG StringLength, PUSHORT Atom) */
+ { &SsNtAddAtom, "NtAddAtom", 3, { WStringArgument, 0, Int16Argument } },
+ /* NTSTATUS NtAlertResumeThread(HANDLE ThreadHandle, PULONG PreviousSuspendCount) */
+ { &SsNtAlertResumeThread, "NtAlertResumeThread", 2, { HandleArgument, 0 } },
+ /* NTSTATUS NtAlertThread(HANDLE ThreadHandle) */
+ { &SsNtAlertThread, "NtAlertThread", 1, { HandleArgument } },
+ /* NTSTATUS NtAllocateLocallyUniqueId(PLUID Luid) */
+ { &SsNtAllocateLocallyUniqueId, "NtAllocateLocallyUniqueId", 1, { 0 } },
+ /* NTSTATUS NtAllocateUserPhysicalPages(HANDLE ProcessHandle, PULONG NumberOfPages, PULONG PageFrameNumbers) */
+ { &SsNtAllocateUserPhysicalPages, "NtAllocateUserPhysicalPages", 3, { HandleArgument, Int32Argument, 0 } },
+ /* NTSTATUS NtAllocateUuids(PLARGE_INTEGER UuidLastTimeAllocated, PULONG UuidDeltaTime, PULONG UuidSequenceNumber,
+ * PUCHAR UuidSeed) */
+ { &SsNtAllocateUuids, "NtAllocateUuids", 4, { Int64Argument, 0, 0, 0 } },
+ /* NTSTATUS NtAllocateVirtualMemory(HANDLE ProcessHandle, PVOID *BaseAddress, ULONG ZeroBits,
+ * PULONG AllocationSize, ULONG AllocationType, ULONG Protect) */
+ { &SsNtAllocateVirtualMemory, "NtAllocateVirtualMemory", 6, { HandleArgument, Int32Argument, 0, Int32Argument, 0, 0 } },
+ /* NTSTATUS NtApphelpCacheControl(APPHELPCACHECONTROL ApphelpCacheControl, PUNICODE_STRING ApphelpCacheObject) */
+ { &SsNtApphelpCacheControl, "NtApphelpCacheControl", 2, { 0, UnicodeStringArgument } },
+ /* NTSTATUS NtAreMappedFilesTheSame(PVOID Address1, PVOID Address2) */
+ { &SsNtAreMappedFilesTheSame, "NtAreMappedFilesTheSame", 2, { 0, 0 } },
+ /* NTSTATUS NtAssignProcessToJobObject(HANDLE JobHandle, HANDLE ProcessHandle) */
+ { &SsNtAssignProcessToJobObject, "NtAssignProcessToJobObject", 2, { HandleArgument, HandleArgument } },
+ /* NTSTATUS NtCallbackReturn(PVOID Result, ULONG ResultLength, NTSTATUS Status) */
+ { &SsNtCallbackReturn, "NtCallbackReturn", 3, { 0, 0, 0 } },
+ /* NTSTATUS NtCancelDeviceWakeupRequest(HANDLE DeviceHandle) */
+ { &SsNtCancelDeviceWakeupRequest, "NtCancelDeviceWakeupRequest", 1, { HandleArgument } },
+ /* NTSTATUS NtCancelIoFile(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock) */
+ { &SsNtCancelIoFile, "NtCancelIoFile", 2, { HandleArgument, 0 } },
+ /* NTSTATUS NtCancelTimer(HANDLE TimerHandle, PBOOLEAN CurrentState) */
+ { &SsNtCancelTimer, "NtCancelTimer", 2, { HandleArgument, 0 } },
+ /* NTSTATUS NtClearEvent(HANDLE EventHandle) */
+ { &SsNtClearEvent, "NtClearEvent", 1, { HandleArgument } },
+ /* NTSTATUS NtClose(HANDLE Handle) */
+ { &SsNtClose, "NtClose", 1, { HandleArgument } },
+ /* NTSTATUS NtContinue(PCONTEXT Context, BOOLEAN TestAlert) */
+ { &SsNtContinue, "NtContinue", 2, { ContextArgument, 0 } },
+ /* NTSTATUS NtCreateDebugObject(PHANDLE DebugObjectHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes,
+ * ULONG Flags) */
+ { &SsNtCreateDebugObject, "NtCreateDebugObject", 4, { 0, 0, ObjectAttributesArgument, 0 } },
+ /* NTSTATUS NtCreateDirectoryObject(PHANDLE DirectoryHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */
+ { &SsNtCreateDirectoryObject, "NtCreateDirectoryObject", 3, { 0, 0, ObjectAttributesArgument } },
+ /* NTSTATUS NtCreateEvent(PHANDLE EventHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes,
+ * EVENT_TYPE EventType, BOOLEAN InitialState) */
+ { &SsNtCreateEvent, "NtCreateEvent", 5, { 0, 0, ObjectAttributesArgument, 0, 0 } },
+ /* NTSTATUS NtCreateEventPair(PHANDLE EventPairHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */
+ { &SsNtCreateEventPair, "NtCreateEventPair", 3, { 0, 0, ObjectAttributesArgument } },
+ /* NTSTATUS NtCreateFile(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes,
+ * PIO_STATUS_BLOCK IoStatusBlock, PLARGE_INTEGER AllocationSize, ULONG FileAttributes,
+ * ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions,
+ * PVOID EaBuffer, ULONG EaLength) */
+ { &SsNtCreateFile, "NtCreateFile", 11, { 0, 0, ObjectAttributesArgument, 0, Int64Argument, 0, 0, 0, 0, 0, 0 } },
+ /* NTSTATUS NtCreateIoCompletion(PHANDLE IoCompletionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes,
+ * ULONG NumberOfConcurrentThreads) */
+ { &SsNtCreateIoCompletion, "NtCreateIoCompletion", 4, { 0, 0, ObjectAttributesArgument, 0 } },
+ /* NTSTATUS NtCreateJobObject(PHANDLE JobHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */
+ { &SsNtCreateJobObject, "NtCreateJobObject", 3, { 0, 0, ObjectAttributesArgument } },
+ /* NTSTATUS NtCreateJobSet(ULONG NumJob, IN PJOB_SET_ARRAY UserJobSet, IN ULONG Flags) */
+ { &SsNtCreateJobSet, "NtCreateJobSet", 3, { 0, 0, 0 } },
+ /* NTSTATUS NtCreateKey(PHANDLE KeyHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes,
+ * ULONG TitleIndex, PUNICODE_STRING Class, ULONG CreateOptions,
+ * PULONG Disposition) */
+ { &SsNtCreateKey, "NtCreateKey", 7, { 0, 0, ObjectAttributesArgument, 0, UnicodeStringArgument, 0, 0 } },
+ /* NTSTATUS NtCreateKeyedEvent(PHANDLE KeyedEventHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes,
+ * ULONG Flags) */
+ { &SsNtCreateKeyedEvent, "NtCreateKeyedEvent", 4, { 0, 0, ObjectAttributesArgument, 0 } },
+ /* NTSTATUS NtCreateMailslotFile(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes,
+ * PIO_STATUS_BLOCK IoStatusBlock, ULONG CreateOptions, ULONG MailslotQuota,
+ * ULONG MaximumMessageSize, PLARGE_INTEGER ReadTimeout) */
+ { &SsNtCreateMailslotFile, "NtCreateMailslotFile", 8, { 0, 0, ObjectAttributesArgument, 0, 0, 0, 0, Int64Argument } },
+ /* NTSTATUS NtCreateMutant(PHANDLE MutantHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes,
+ * BOOLEAN InitialOwner) */
+ { &SsNtCreateMutant, "NtCreateMutant", 4, { 0, 0, ObjectAttributesArgument, 0 } },
+ /* NTSTATUS NtCreateNamedPipeFile(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes,
+ * PIO_STATUS_BLOCK IoStatusBlock, ULONG ShareAccess, ULONG CreateDisposition,
+ * ULONG CreateOptions, BOOLEAN TypeMessage, BOOLEAN ReadmodeMessage,
+ * BOOLEAN Nonblocking, ULONG MaxInstances, ULONG InBufferSize,
+ * ULONG OutBufferSize, PLARGE_INTEGER DefaultTimeout) */
+ { &SsNtCreateNamedPipeFile, "NtCreateNamedPipeFile", 14, { 0, 0, ObjectAttributesArgument, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Int64Argument } },
+ /* NTSTATUS NtCreatePagingFile(PUNICODE_STRING FileName, PULARGE_INTEGER MinimumSize, PULARGE_INTEGER MaximumSize,
+ * ULONG Priority) */
+ { &SsNtCreatePagingFile, "NtCreatePagingFile", 4, { UnicodeStringArgument, Int64Argument, Int64Argument, 0 } },
+ /* NTSTATUS NtCreatePort(PHANDLE PortHandle, POBJECT_ATTRIBUTES ObjectAttributes, ULONG MaxConnectionInfoLength,
+ * ULONG MaxMessageLength, ULONG MaxPoolUsage) */
+ { &SsNtCreatePort, "NtCreatePort", 5, { 0, ObjectAttributesArgument, 0, 0, 0 } },
+ /* NTSTATUS NtCreatePrivateNamespace(PHANDLE PrivateNamespaceHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes,
+ * PBOUNDARY_DESCRIPTOR BoundaryDescriptor) */
+ { &SsNtCreatePrivateNamespace, "NtCreatePrivateNamespace", 4, { 0, 0, ObjectAttributesArgument, 0 } },
+ /* NTSTATUS NtCreateProcess(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes,
+ * HANDLE InheritFromProcessHandle, BOOLEAN InheritHandles, HANDLE SectionHandle,
+ * HANDLE DebugPort, HANDLE ExceptionPort) */
+ { &SsNtCreateProcess, "NtCreateProcess", 8, { 0, 0, ObjectAttributesArgument, HandleArgument, 0, HandleArgument, HandleArgument, HandleArgument } },
+ /* NTSTATUS NtCreateProcessEx(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes,
+ * HANDLE ParentProcess, ULONG Flags, HANDLE SectionHandle,
+ * HANDLE DebugPort, HANDLE ExceptionPort, ULONG JobMemberLevel */
+ { &SsNtCreateProcessEx, "NtCreateProcessEx", 9, { 0, 0, ObjectAttributesArgument, HandleArgument, 0, HandleArgument, HandleArgument, HandleArgument, 0 } },
+ /* NTSTATUS NtCreateProfile(PHANDLE ProfileHandle, HANDLE ProcessHandle, PVOID Base,
+ * ULONG Size, ULONG BucketShift, PULONG Buffer,
+ * ULONG BufferLength, KPROFILE_SOURCE Source, ULONG ProcessorMask) */
+ { &SsNtCreateProfile, "NtCreateProfile", 9, { 0, HandleArgument, 0, 0, 0, 0, 0, 0, 0 } },
+ /* NTSTATUS NtCreateSection(PHANDLE SectionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes,
+ * PLARGE_INTEGER SectionSize, ULONG Protect, ULONG Attributes,
+ * HANDLE FileHandle) */
+ { &SsNtCreateSection, "NtCreateSection", 7, { 0, 0, ObjectAttributesArgument, Int64Argument, 0, 0, HandleArgument } },
+ /* NTSTATUS NtCreateSemaphore(PHANDLE SemaphoreHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes,
+ * LONG InitialCount, LONG MaximumCount) */
+ { &SsNtCreateSemaphore, "NtCreateSemaphore", 5, { 0, 0, ObjectAttributesArgument, 0, 0 } },
+ /* NTSTATUS NtCreateSymbolicLinkObject(PHANDLE SymbolicLinkHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes,
+ * PUNICODE_STRING TargetName) */
+ { &SsNtCreateSymbolicLinkObject, "NtCreateSymbolicLinkObject", 4, { 0, 0, ObjectAttributesArgument, UnicodeStringArgument } },
+ /* NTSTATUS NtCreateThread(PHANDLE ThreadHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes,
+ * HANDLE ProcessHandle, PCLIENT_ID ClientId, PCONTEXT ThreadContext,
+ * PINITIAL_TEB UserStack, BOOLEAN CreateSuspended) */
+ { &SsNtCreateThread, "NtCreateThread", 8, { 0, 0, ObjectAttributesArgument, HandleArgument, 0, ContextArgument, InitialTebArgument, 0 } },
+ /* NTSTATUS NtCreateTimer(PHANDLE TimerHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes,
+ * TIMER_TYPE TimerType) */
+ { &SsNtCreateTimer, "NtCreateTimer", 4, { 0, 0, ObjectAttributesArgument, 0 } },
+ /* NTSTATUS NtCreateToken(PHANDLE TokenHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes,
+ * TOKEN_TYPE Type, PLUID AuthenticationId, PLARGE_INTEGER ExpirationTime,
+ * PTOKEN_USER User, PTOKEN_GROUPS Groups, PTOKEN_PRIVILEGES Privileges,
+ * PTOKEN_OWNER Owner, PTOKEN_PRIMARY_GROUP PrimaryGroup, PTOKEN_DEFAULT_DACL DefaultDacl,
+ * PTOKEN_SOURCE Source) */
+ { &SsNtCreateToken, "NtCreateToken", 13, { 0, 0, ObjectAttributesArgument, 0, Int64Argument, Int64Argument, 0, 0, 0, 0, 0, 0, 0 } },
+ /* NTSTATUS NtCreateWaitablePort(PHANDLE PortHandle, POBJECT_ATTRIBUTES ObjectAttributes, ULONG MaxConnectionInfoLength,
+ * ULONG MaxMessageLength, ULONG MaxPoolUsage) */
+ { &SsNtCreateWaitablePort, "NtCreateWaitablePort", 5, { 0, ObjectAttributesArgument, 0, 0, 0 } },
+ /* NTSTATUS NtDebugActiveProcess(HANDLE ProcessHandle, HANDLE DebugObjectHandle) */
+ { &SsNtDebugActiveProcess, "NtDebugActiveProcess", 2, { HandleArgument, HandleArgument } },
+ /* NTSTATUS NtDebugContinue(HANDLE DebugObjectHandle, PCLIENT_ID ClientId, NTSTATUS ContinueStatus) */
+ { &SsNtDebugContinue, "NtDebugContinue", 3, { HandleArgument, ClientIdArgument, 0 } },
+ /* NTSTATUS NtDelayExecution(BOOLEAN Alertable, PLARGE_INTEGER Interval) */
+ { &SsNtDelayExecution, "NtDelayExecution", 2, { 0, Int64Argument } },
+ /* NTSTATUS NtDeleteAtom(USHORT Atom) */
+ { &SsNtDeleteAtom, "NtDeleteAtom", 1, { 0 } },
+ /* NTSTATUS NtDeleteBootEntry(ULONG Id) */
+ { &SsNtDeleteBootEntry, "NtDeleteBootEntry", 1, { 0 } },
+ /* NTSTATUS NtDeleteDriverEntry(ULONG Id) */
+ { &SsNtDeleteDriverEntry, "NtDeleteDriverEntry", 1, { 0 } },
+ /* NTSTATUS NtDeleteFile(POBJECT_ATTRIBUTES ObjectAttributes) */
+ { &SsNtDeleteFile, "NtDeleteFile", 1, { ObjectAttributesArgument } },
+ /* NTSTATUS NtDeleteKey(HANDLE KeyHandle) */
+ { &SsNtDeleteKey, "NtDeleteKey", 1, { HandleArgument } },
+ /* NTSTATUS NtDeleteObjectAuditAlarm(PUNICODE_STRING SubsystemName, PVOID HandleId, BOOLEAN GenerateOnClose) */
+ { &SsNtDeleteObjectAuditAlarm, "NtDeleteObjectAuditAlarm", 3, { UnicodeStringArgument, 0, 0 } },
+ /* NTSTATUS NtDeletePrivateNamespace(HANDLE PrivateNamespaceHandle) */
+ { &SsNtDeletePrivateNamespace, "NtDeletePrivateNamespace", 1, { HandleArgument } },
+ /* NTSTATUS NtDeleteValueKey(HANDLE KeyHandle, PUNICODE_STRING ValueName) */
+ { &SsNtDeleteValueKey, "NtDeleteValueKey", 2, { HandleArgument, UnicodeStringArgument } },
+ /* NTSTATUS NtDeviceIoControlFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine,
+ * PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, ULONG IoControlCode,
+ * PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer,
+ * ULONG OutputBufferLength) */
+ { &SsNtDeviceIoControlFile, "NtDeviceIoControlFile", 10, { HandleArgument, HandleArgument, 0, 0, 0, 0, 0, 0, 0, 0 } },
+ /* NTSTATUS NtDisplayString(PUNICODE_STRING String) */
+ { &SsNtDisplayString, "NtDisplayString", 1, { UnicodeStringArgument } },
+ /* NTSTATUS NtDuplicateObject(HANDLE SourceProcessHandle, HANDLE SourceHandle, HANDLE TargetProcessHandle,
+ * PHANDLE TargetHandle, ACCESS_MASK DesiredAccess, ULONG Attributes,
+ * ULONG Options) */
+ { &SsNtDuplicateObject, "NtDuplicateObject", 7, { HandleArgument, HandleArgument, HandleArgument, 0, 0, 0, 0 } },
+ /* NTSTATUS NtDuplicateToken(HANDLE ExistingTokenHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes,
+ * BOOLEAN EffectiveOnly, TOKEN_TYPE TokenType, PHANDLE NewTokenHandle) */
+ { &SsNtDuplicateToken, "NtDuplicateToken", 6, { HandleArgument, 0, ObjectAttributesArgument, 0, 0, 0 } },
+ /* NTSTATUS NtEnumerateBootEntries(PVOID Buffer, PULONG BufferLength) */
+ { &SsNtEnumerateBootEntries, "NtEnumerateBootEntries", 2, { 0, Int32Argument } },
+ /* NTSTATUS NtEnumerateDriverEntries(PVOID Buffer, PULONG BufferLength) */
+ { &SsNtEnumerateDriverEntries, "NtEnumerateDriverEntries", 2, { 0, Int32Argument } },
+ /* NTSTATUS NtEnumerateKey(HANDLE KeyHandle, ULONG Index, KEY_INFORMATION_CLASS KeyInformationClass,
+ * PVOID KeyInformation, ULONG KeyInformationLength, PULONG ResultLength) */
+ { &SsNtEnumerateKey, "NtEnumerateKey", 6, { HandleArgument, 0, 0, 0, 0, 0 } },
+ /* NTSTATUS NtEnumerateSystemEnvironmentValuesEx(ULONG InformationClass, PVOID Buffer, PULONG BufferLength) */
+ { &SsNtEnumerateSystemEnvironmentValuesEx, "NtEnumerateSystemEnvironmentValuesEx", 3, { 0, 0, Int32Argument } },
+ /* NTSTATUS NtEnumerateValueKey(HANDLE KeyHandle, ULONG Index, KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
+ * PVOID KeyValueInformation, ULONG KeyValueInformationLength, PULONG ResultLength) */
+ { &SsNtEnumerateValueKey, "NtEnumerateValueKey", 6, { HandleArgument, 0, 0, 0, 0, 0 } },
+ /* NTSTATUS NtExtendSection(HANDLE SectionHandle, PLARGE_INTEGER SectionSize) */
+ { &SsNtExtendSection, "NtExtendSection", 2, { HandleArgument, Int64Argument } },
+ /* NTSTATUS NtFilterToken(HANDLE ExistingTokenHandle, ULONG Flags, PTOKEN_GROUPS SidsToDisable,
+ * PTOKEN_PRIVILEGES PrivilegesToDelete, PTOKEN_GROUPS SidsToRestricted, PHANDLE NewTokenHandle) */
+ { &SsNtFilterToken, "NtFilterToken", 6, { HandleArgument, 0, 0, 0, 0, 0 } },
+ /* NTSTATUS NtFindAtom(PWSTR String, ULONG StringLength, PUSHORT Atom) */
+ { &SsNtFindAtom, "NtFindAtom", 3, { WStringArgument, 0, 0 } },
+ /* NTSTATUS NtFlushBuffersFile(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock) */
+ { &SsNtFlushBuffersFile, "NtFlushBuffersFile", 2, { HandleArgument, 0 } },
+ /* NTSTATUS NtFlushInstructionCache(HANDLE ProcessHandle, PVOID BaseAddress, ULONG FlushSize) */
+ { &SsNtFlushInstructionCache, "NtFlushInstructionCache", 3, { HandleArgument, 0, 0 } },
+ /* NTSTATUS NtFlushKey(HANDLE KeyHandle) */
+ { &SsNtFlushKey, "NtFlushKey", 1, { HandleArgument } },
+ /* NTSTATUS NtFlushProcessWriteBuffers() */
+ { &SsNtFlushProcessWriteBuffers, "NtFlushProcessWriteBuffers", 0 },
+ /* NTSTATUS NtFlushVirtualMemory(HANDLE ProcessHandle, PVOID *BaseAddress, PULONG FlushSize,
+ * PIO_STATUS_BLOCK IoStatusBlock) */
+ { &SsNtFlushVirtualMemory, "NtFlushVirtualMemory", 4, { HandleArgument, Int32Argument, Int32Argument, 0 } },
+ /* NTSTATUS NtFlushWriteBuffer() */
+ { &SsNtFlushWriteBuffer, "NtFlushWriteBuffer", 0 },
+ /* NTSTATUS NtFreeUserPhysicalPages(HANDLE ProcessHandle, PULONG NumberOfPages, PULONG PageFrameNumbers) */
+ { &SsNtFreeUserPhysicalPages, "NtFreeUserPhysicalPages", 3, { HandleArgument, Int32Argument, 0 } },
+ /* NTSTATUS NtFreeVirtualMemory(HANDLE ProcessHandle, PVOID *BaseAddress, PULONG FreeSize,
+ * ULONG FreeType) */
+ { &SsNtFreeVirtualMemory, "NtFreeVirtualMemory", 4, { HandleArgument, Int32Argument, Int32Argument, 0 } },
+ /* NTSTATUS NtFsControlFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine,
+ * PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, ULONG FsControlCode,
+ * PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer,
+ * ULONG OutputBufferLength) */
+ { &SsNtFsControlFile, "NtFsControlFile", 10, { HandleArgument, HandleArgument, 0, 0, 0, 0, 0, 0, 0, 0 } },
+ /* NTSTATUS NtGetContextThread(HANDLE ThreadHandle, PCONTEXT Context) */
+ { &SsNtGetContextThread, "NtGetContextThread", 2, { HandleArgument, ContextArgument } },
+ /* NTSTATUS NtGetCurrentProcessorNumber() */
+ { &SsNtGetCurrentProcessorNumber, "NtGetCurrentProcessorNumber", 0 },
+ /* NTSTATUS NtGetDevicePowerState(HANDLE DeviceHandle, PDEVICE_POWER_STATE DevicePowerState) */
+ { &SsNtGetDevicePowerState, "NtGetDevicePowerState", 2, { HandleArgument, 0 } },
+ /* NTSTATUS NtGetNextProcess(HANDLE ProcessHandle, ACCESS_MASK DesiredAccess, ULONG HandleAttributes,
+ * ULONG Flags, PHANDLE NewProcessHandle) */
+ { &SsNtGetNextProcess, "NtGetNextProcess", 5, { HandleArgument, 0, 0, 0, 0 } },
+ /* NTSTATUS NtGetNextThread(HANDLE ProcessHandle, HANDLE ThreadHandle, ACCESS_MASK DesiredAccess,
+ * ULONG HandleAttributes, ULONG Flags, PHANDLE NewThreadHandle) */
+ { &SsNtGetNextThread, "NtGetNextThread", 6, { HandleArgument, HandleArgument, 0, 0, 0, 0 } },
+ /* NTSTATUS NtGetPlugPlayEvent(HANDLE EventHandle, PVOID Context, PVOID Buffer,
+ * ULONG BufferLength) */
+ { &SsNtGetPlugPlayEvent, "NtGetPlugPlayEvent", 4, { HandleArgument, 0, 0, 0 } },
+ /* NTSTATUS NtGetWriteWatch(HANDLE ProcessHandle, ULONG Flags, PVOID BaseAddress,
+ * ULONG RegionSize, PULONG Buffer, PULONG BufferEntries,
+ * PULONG Granularity) */
+ { &SsNtGetWriteWatch, "NtGetWriteWatch", 7, { HandleArgument, 0, 0, 0, 0, 0, 0 } },
+ /* NTSTATUS NtImpersonateAnonymousToken(HANDLE ThreadHandle) */
+ { &SsNtImpersonateAnonymousToken, "NtImpersonateAnonymousToken", 1, { HandleArgument } },
+ /* NTSTATUS NtImpersonateClientOfPort(HANDLE PortHandle, PPORT_MESSAGE Message) */
+ { &SsNtImpersonateClientOfPort, "SsNtImpersonateClientOfPort", 2, { HandleArgument, 0 } },
+ /* NTSTATUS NtImpersonateThread(HANDLE ThreadHandle, HANDLE TargetThreadHandle, PSECURITY_QUALITY_OF_SERVICE SecurityQos) */
+ { &SsNtImpersonateThread, "NtImpersonateThread", 3, { HandleArgument, HandleArgument, 0 } },
+ /* NTSTATUS NtInitiatePowerAction(POWER_ACTION SystemAction, SYSTEM_POWER_STATE MinSystemState, ULONG Flags,
+ * BOOLEAN Asynchronous) */
+ { &SsNtInitiatePowerAction, "NtInitiatePowerAction", 4, { 0, 0, 0, 0 } },
+ /* NTSTATUS NtIsProcessInJob(HANDLE ProcessHandle, HANDLE JobHandle) */
+ { &SsNtIsProcessInJob, "NtIsProcessInJob", 2, { HandleArgument, HandleArgument } },
+ /* NTSTATUS NtIsSystemResumeAutomatic() */
+ { &SsNtIsSystemResumeAutomatic, "NtIsSystemResumeAutomatic", 0 },
+ /* NTSTATUS NtListenPort(HANDLE PortHandle, PPORT_MESSAGE Message) */
+ { &SsNtListenPort, "NtListenPort", 2, { HandleArgument, 0 } },
+ /* NTSTATUS NtLoadDriver(PUNICODE_STRING DriverServiceName) */
+ { &SsNtLoadDriver, "NtLoadDriver", 1, { UnicodeStringArgument } },
+ /* NTSTATUS NtLoadKey(POBJECT_ATTRIBUTES KeyObjectAttributes, POBJECT_ATTRIBUTES FileObjectAttributes) */
+ { &SsNtLoadKey, "NtLoadKey", 2, { ObjectAttributesArgument, ObjectAttributesArgument } },
+ /* NTSTATUS NtLoadKey2(POBJECT_ATTRIBUTES KeyObjectAttributes, POBJECT_ATTRIBUTES FileObjectAttributes, ULONG Flags) */
+ { &SsNtLoadKey2, "NtLoadKey2", 3, { ObjectAttributesArgument, ObjectAttributesArgument, 0 } },
+ /* NTSTATUS NtLockFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine,
+ * PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, PULARGE_INTEGER LockOffset,
+ * PULARGE_INTEGER LockLength, ULONG Key, BOOLEAN FailImmediately,
+ * BOOLEAN ExclusiveLock) */
+ { &SsNtLockFile, "NtLockFile", 10, { HandleArgument, HandleArgument, 0, 0, 0, Int64Argument, Int64Argument, 0, 0, 0 } },
+ /* NTSTATUS NtLockVirtualMemory(HANDLE ProcessHandle, PVOID *BaseAddress, PULONG LockSize,
+ * ULONG LockType) */
+ { &SsNtLockVirtualMemory, "NtLockVirtualMemory", 4, { HandleArgument, Int32Argument, Int32Argument, 0 } },
+ /* NTSTATUS NtMakePermanentObject(HANDLE Handle) */
+ { &SsNtMakePermanentObject, "NtMakePermanentObject", 1, { HandleArgument } },
+ /* NTSTATUS NtMakeTemporaryObject(HANDLE Handle) */
+ { &SsNtMakeTemporaryObject, "NtMakeTemporaryObject", 1, { HandleArgument } },
+ /* NTSTATUS NtMapUserPhysicalPages(PVOID BaseAddress, PULONG NumberOfPages, PULONG PageFrameNumbers) */
+ { &SsNtMapUserPhysicalPages, "NtMapUserPhysicalPages", 3, { 0, Int32Argument, 0 } },
+ /* NTSTATUS NtMapUserPhysicalPagesScatter(PVOID BaseAddress, PULONG NumberOfPages, PULONG PageFrameNumbers) */
+ { &SsNtMapUserPhysicalPagesScatter, "NtMapUserPhysicalPagesScatter", 3, { 0, Int32Argument, 0 } },
+ /* NTSTATUS NtMapViewOfSection(HANDLE SectionHandle, HANDLE ProcessHandle, PVOID *BaseAddress,
+ * ULONG ZeroBits, ULONG CommitSize, PLARGE_INTEGER SectionOffset,
+ * PULONG ViewSize, SECTION_INHERIT InheritDisposition, ULONG AllocationType,
+ * ULONG Protect) */
+ { &SsNtMapViewOfSection, "NtMapViewOfSection", 10, { HandleArgument, HandleArgument, Int32Argument, 0, 0, Int64Argument, Int32Argument, 0, 0, 0 } },
+ /* NTSTATUS NtModifyBootEntry(PBOOT_ENTRY BootEntry) */
+ { &SsNtModifyBootEntry, "NtModifyBootEntry", 1, { 0 } },
+ /* NTSTATUS NtModifyDriverEntry(PEFI_DRIVER_ENTRY DriverEntry) */
+ { &SsNtModifyDriverEntry, "NtModifyDriverEntry", 1, { 0 } },
+ /* NTSTATUS NtNotifyChangeDirectoryFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine,
+ * PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, PFILE_NOTIFY_INFORMATION Buffer,
+ * ULONG BufferLength, ULONG NotifyFilter, BOOLEAN WatchSubtree) */
+ { &SsNtNotifyChangeDirectoryFile, "NtNotifyChangeDirectoryFile", 9, { HandleArgument, HandleArgument, 0, 0, 0, 0, 0, 0, 0 } },
+ /* NTSTATUS NtNotifyChangeKey(HANDLE KeyHandle, HANDLE EventHandle, PIO_APC_ROUTINE ApcRoutine,
+ * PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, ULONG NotifyFilter,
+ * BOOLEAN WatchSubtree, PVOID Buffer, ULONG BufferLength,
+ * BOOLEAN Asynchronous) */
+ { &SsNtNotifyChangeKey, "NtNotifyChangeKey", 10, { HandleArgument, HandleArgument, 0, 0, 0, 0, 0, 0, 0, 0 } },
+ /* NTSTATUS NtNotifyChangeMultipleKeys(HANDLE KeyHandle, ULONG Flags, POBJECT_ATTRIBUTES KeyObjectAttributes,
+ * HANDLE EventHandle, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext,
+ * PIO_STATUS_BLOCK IoStatusBlock, ULONG NotifyFilter, BOOLEAN WatchSubtree,
+ * PVOID Buffer, ULONG BufferLength, BOOLEAN Asynchronous) */
+ { &SsNtNotifyChangeMultipleKeys, "NtNotifyChangeMultipleKeys", 12, { HandleArgument, 0, ObjectAttributesArgument, HandleArgument, 0, 0, 0, 0, 0, 0, 0, 0 } },
+ /* NTSTATUS NtOpenDirectoryObject(PHANDLE DirectoryHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */
+ { &SsNtOpenDirectoryObject, "NtOpenDirectoryObject", 3, { 0, 0, ObjectAttributesArgument } },
+ /* NTSTATUS NtOpenEvent(PHANDLE EventHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */
+ { &SsNtOpenEvent, "NtOpenEvent", 3, { 0, 0, ObjectAttributesArgument } },
+ /* NTSTATUS NtOpenEventPair(PHANDLE EventPairHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */
+ { &SsNtOpenEventPair, "NtOpenEventPair", 3, { 0, 0, ObjectAttributesArgument } },
+ /* NTSTATUS NtOpenFile(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes,
+ * PIO_STATUS_BLOCK IoStatusBlock, ULONG ShareAccess, ULONG OpenOptions) */
+ { &SsNtOpenFile, "NtOpenFile", 6, { 0, 0, ObjectAttributesArgument, 0, 0, 0 } },
+ /* NTSTATUS NtOpenIoCompletion(PHANDLE IoCompletionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */
+ { &SsNtOpenIoCompletion, "NtOpenIoCompletion", 3, { 0, 0, ObjectAttributesArgument } },
+ /* NTSTATUS NtOpenJobObject(PHANDLE JobHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */
+ { &SsNtOpenJobObject, "NtOpenJobObject", 3, { 0, 0, ObjectAttributesArgument } },
+ /* NTSTATUS NtOpenKey(PHANDLE KeyHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */
+ { &SsNtOpenKey, "NtOpenKey", 3, { 0, 0, ObjectAttributesArgument } },
+ /* NTSTATUS NtOpenKeyedEvent(PHANDLE KeyedEventHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */
+ { &SsNtOpenKeyedEvent, "NtOpenKeyedEvent", 3, { 0, 0, ObjectAttributesArgument } },
+ /* NTSTATUS NtOpenMutant(PHANDLE MutantHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */
+ { &SsNtOpenMutant, "NtOpenMutant", 3, { 0, 0, ObjectAttributesArgument } },
+ /* NTSTATUS NtOpenObjectAuditAlarm(PUNICODE_STRING SubsystemName, PVOID *HandleId, PUNICODE_STRING ObjectTypeName,
+ * PUNICODE_STRING ObjectName, PSECURITY_DESCRIPTOR SecurityDescriptor, HANDLE TokenHandle,
+ * ACCESS_MASK DesiredAccess, ACCESS_MASK GrantedAccess, PPRIVILEGE_SET Privileges,
+ * BOOLEAN ObjectCreation, BOOLEAN AccessGranted, PBOOLEAN GenerateOnClose) */
+ { &SsNtOpenObjectAuditAlarm, "NtOpenObjectAuditAlarm", 12, { UnicodeStringArgument, Int32Argument, UnicodeStringArgument, UnicodeStringArgument, 0, HandleArgument, 0, 0, 0, 0, 0, 0 } },
+ /* NTSTATUS NtOpenProcess(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes,
+ * PCLIENT_ID ClientId) */
+ { &SsNtOpenProcess, "NtOpenProcess", 4, { 0, 0, ObjectAttributesArgument, ClientIdArgument } },
+ /* NTSTATUS NtOpenProcessToken(HANDLE ProcessHandle, ACCESS_MASK DesiredAccess, PHANDLE TokenHandle) */
+ { &SsNtOpenProcessToken, "NtOpenProcessToken", 3, { HandleArgument, 0, 0 } },
+ /* NTSTATUS NtOpenProcessTokenEx(HANDLE ProcessHandle, ACCESS_MASK DesiredAccess, ULONG HandleAttributes,
+ * PHANDLE TokenHandle) */
+ { &SsNtOpenProcessTokenEx, "NtOpenProcessTokenEx", 4, { HandleArgument, 0, 0, 0 } },
+ /* NTSTATUS NtOpenSection(PHANDLE SectionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */
+ { &SsNtOpenSection, "NtOpenSection", 3, { 0, 0, ObjectAttributesArgument } },
+ /* NTSTATUS NtOpenSemaphore(PHANDLE SemaphoreHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */
+ { &SsNtOpenSemaphore, "NtOpenSemaphore", 3, { 0, 0, ObjectAttributesArgument } },
+ /* NTSTATUS NtOpenSymbolicLinkObject(PHANDLE SymbolicLinkHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */
+ { &SsNtOpenSymbolicLinkObject, "NtOpenSymbolicLinkObject", 3, { 0, 0, ObjectAttributesArgument } },
+ /* NTSTATUS NtOpenThread(PHANDLE ThreadHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes,
+ * PCLIENT_ID ClientId) */
+ { &SsNtOpenThread, "NtOpenThread", 4, { 0, 0, ObjectAttributesArgument, ClientIdArgument } },
+ /* NTSTATUS NtOpenThreadToken(HANDLE ThreadHandle, ACCESS_MASK DesiredAccess, BOOLEAN OpenAsSelf,
+ * PHANDLE TokenHandle) */
+ { &SsNtOpenThreadToken, "NtOpenThreadToken", 4, { HandleArgument, 0, 0, 0 } },
+ /* NTSTATUS NtOpenThreadTokenEx(HANDLE ThreadHandle, ACCESS_MASK DesiredAccess, BOOLEAN OpenAsSelf,
+ * ULONG HandleAttributes, PHANDLE TokenHandle) */
+ { &SsNtOpenThreadTokenEx, "NtOpenThreadTokenEx", 5, { HandleArgument, 0, 0, 0, 0 } },
+ /* NTSTATUS NtOpenTimer(PHANDLE TimerHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) */
+ { &SsNtOpenTimer, "NtOpenTimer", 3, { 0, 0, ObjectAttributesArgument } },
+ /* NTSTATUS NtReadFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine,
+ * PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, PVOID Buffer,
+ * ULONG Length, PLARGE_INTEGER ByteOffset, PULONG Key) */
+ { &SsNtReadFile, "NtReadFile", 9, { HandleArgument, HandleArgument, 0, 0, 0, 0, 0, Int64Argument, Int32Argument } },
+ /* NTSTATUS NtWriteFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine,
+ * PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, PVOID Buffer,
+ * ULONG Length, PLARGE_INTEGER ByteOffset, PULONG Key) */
+ { &SsNtWriteFile, "NtWriteFile", 9, { HandleArgument, HandleArgument, 0, 0, 0, 0, 0, Int64Argument, Int32Argument } },
+
+ { NULL, "Dummy", 0 }
+};
+
+RTL_GENERIC_TABLE KphSsCallTable;
+FAST_MUTEX KphSsCallTableMutex;
+
+/* KphSsDataInit
+ *
+ * Initializes all data structures so that system service entries
+ * can be looked up.
+ */
+VOID KphSsDataInit()
+{
+ ULONG i;
+
+ RtlInitializeGenericTable(
+ &KphSsCallTable,
+ KphpSsCallEntryCompareRoutine,
+ KphpSsCallEntryAllocateRoutine,
+ KphpSsCallEntryFreeRoutine,
+ NULL
+ );
+
+ for (i = 0; i < sizeof(SsEntries) / sizeof(KPHSS_CALL_ENTRY); i++)
+ {
+ /* Ignore the dummy entry. */
+ if (SsEntries[i].Number)
+ {
+ RtlInsertElementGenericTable(
+ &KphSsCallTable,
+ &SsEntries[i],
+ /* Save some space... */
+ FIELD_OFFSET(KPHSS_CALL_ENTRY, Arguments) +
+ SsEntries[i].NumberOfArguments * sizeof(KPHSS_ARGUMENT_TYPE),
+ NULL
+ );
+ }
+ }
+
+ ExInitializeFastMutex(&KphSsCallTableMutex);
+}
+
+/* KphSsDataDeinit
+ *
+ * Frees all memory associated with system service data.
+ */
+VOID KphSsDataDeinit()
+{
+ PKPHSS_CALL_ENTRY callEntry;
+
+ while (callEntry = (PKPHSS_CALL_ENTRY)RtlGetElementGenericTable(&KphSsCallTable, 0))
+ RtlDeleteElementGenericTable(&KphSsCallTable, callEntry);
+}
+
+/* KphSsLookupCallEntry
+ *
+ * Lookups up a system service entry by system service number.
+ */
+PKPHSS_CALL_ENTRY KphSsLookupCallEntry(
+ __in ULONG Number
+ )
+{
+ KPHSS_CALL_ENTRY callEntry;
+ PKPHSS_CALL_ENTRY foundEntry;
+
+ callEntry.Number = &Number;
+
+ ExAcquireFastMutex(&KphSsCallTableMutex);
+ foundEntry = (PKPHSS_CALL_ENTRY)RtlLookupElementGenericTable(
+ &KphSsCallTable,
+ &callEntry
+ );
+ ExReleaseFastMutex(&KphSsCallTableMutex);
+
+ return foundEntry;
+}
+
+/* KphpSsCallEntryAllocateRoutine
+ *
+ * Allocates storage for a system service entry.
+ */
+PVOID KphpSsCallEntryAllocateRoutine(
+ __in PRTL_GENERIC_TABLE Table,
+ __in CLONG ByteSize
+ )
+{
+ return ExAllocatePoolWithTag(
+ PagedPool,
+ ByteSize,
+ TAG_CALL_ENTRY
+ );
+}
+
+/* KphpSsCallEntryCompareRoutine
+ *
+ * Compares two system service entries.
+ */
+RTL_GENERIC_COMPARE_RESULTS KphpSsCallEntryCompareRoutine(
+ __in PRTL_GENERIC_TABLE Table,
+ __in PVOID FirstStruct,
+ __in PVOID SecondStruct
+ )
+{
+ PKPHSS_CALL_ENTRY callEntry1, callEntry2;
+
+ callEntry1 = (PKPHSS_CALL_ENTRY)FirstStruct;
+ callEntry2 = (PKPHSS_CALL_ENTRY)SecondStruct;
+
+ if (*(callEntry1->Number) < *(callEntry2->Number))
+ return GenericLessThan;
+ else if (*(callEntry1->Number) > *(callEntry2->Number))
+ return GenericGreaterThan;
+ else
+ return GenericEqual;
+}
+
+/* KphpSsCallEntryFreeRoutine
+ *
+ * Frees storage for a system service entry.
+ */
+VOID KphpSsCallEntryFreeRoutine(
+ __in PRTL_GENERIC_TABLE Table,
+ __in PVOID Buffer
+ )
+{
+ ExFreePoolWithTag(
+ Buffer,
+ TAG_CALL_ENTRY
+ );
+}
diff --git a/branches/ph-plugins/KProcessHacker/test.c b/branches/ph-plugins/KProcessHacker/test.c
new file mode 100644
index 000000000..ffc9d6016
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/test.c
@@ -0,0 +1,71 @@
+/*
+ * Process Hacker Driver -
+ * testing code
+ *
+ * Copyright (C) 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 .
+ */
+
+#include "include/kph.h"
+
+static EX_PUSH_LOCK TestLock;
+
+VOID KphpTestPushLockThreadStart(
+ __in PVOID Context
+ );
+
+VOID KphTestPushLock()
+{
+ ULONG i;
+
+ ExInitializePushLock(&TestLock);
+
+ for (i = 0; i < 10; i++)
+ {
+ HANDLE threadHandle;
+ OBJECT_ATTRIBUTES objectAttributes;
+
+ InitializeObjectAttributes(&objectAttributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
+ PsCreateSystemThread(&threadHandle, 0, &objectAttributes, NULL, NULL, KphpTestPushLockThreadStart, NULL);
+ }
+}
+
+VOID KphpTestPushLockThreadStart(
+ __in PVOID Context
+ )
+{
+ ULONG i, j;
+
+ for (i = 0; i < 400000; i++)
+ {
+ ExAcquirePushLockShared(&TestLock);
+
+ for (j = 0; j < 1000; j++)
+ YieldProcessor();
+
+ ExReleasePushLock(&TestLock);
+
+ ExAcquirePushLockExclusive(&TestLock);
+
+ for (j = 0; j < 9000; j++)
+ YieldProcessor();
+
+ ExReleasePushLock(&TestLock);
+ }
+
+ PsTerminateSystemThread(STATUS_SUCCESS);
+}
diff --git a/branches/ph-plugins/KProcessHacker/trace.c b/branches/ph-plugins/KProcessHacker/trace.c
new file mode 100644
index 000000000..57fc98ede
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/trace.c
@@ -0,0 +1,344 @@
+/*
+ * Process Hacker Driver -
+ * stack tracing
+ *
+ * Copyright (C) 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 .
+ */
+
+#include "include/kph.h"
+
+BOOLEAN KphpCaptureAndAddStack(
+ __in PRTL_TRACE_DATABASE Database,
+ __in KPH_CAPTURE_AND_ADD_STACK_TYPE Type,
+ __out_opt PRTL_TRACE_BLOCK *TraceBlock
+ );
+
+VOID KphpTraceDatabaseDeleteProcedure(
+ __in PVOID Object,
+ __in ULONG Flags
+ );
+
+PKPH_OBJECT_TYPE KphTraceDatabaseType;
+
+/* KphTraceDatabaseInitialization
+ *
+ * Creates the TraceDatabase object type.
+ */
+NTSTATUS KphTraceDatabaseInitialization()
+{
+ NTSTATUS status = STATUS_SUCCESS;
+
+ status = KphCreateObjectType(
+ &KphTraceDatabaseType,
+ PagedPool,
+ 0,
+ KphpTraceDatabaseDeleteProcedure
+ );
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ return status;
+}
+
+/* KphCaptureStackBackTrace
+ *
+ * Walks the stack, capturing the return address from each frame.
+ *
+ * Return value: the number of captured addresses in the buffer.
+ */
+ULONG KphCaptureStackBackTrace(
+ __in ULONG FramesToSkip,
+ __in ULONG FramesToCapture,
+ __in_opt ULONG Flags,
+ __out_ecount(FramesToCapture) PVOID *BackTrace,
+ __out_opt PULONG BackTraceHash
+ )
+{
+ PVOID backTrace[MAX_STACK_DEPTH];
+ ULONG framesFound;
+ ULONG hash;
+ ULONG i;
+
+ /* Skip the current frame (for this function). */
+ FramesToSkip++;
+
+ /* Check the input. */
+ /* Ensure we won't overrun the buffer. */
+ if (FramesToCapture + FramesToSkip > MAX_STACK_DEPTH)
+ return 0;
+ /* Make sure the flags are correct. */
+ if ((Flags & RTL_WALK_VALID_FLAGS) != Flags)
+ return 0;
+
+ /* Walk the frame chain. */
+ framesFound = RtlWalkFrameChain(
+ backTrace,
+ FramesToCapture + FramesToSkip,
+ Flags
+ );
+ /* Return if we found fewer frames than we wanted to skip. */
+ if (framesFound <= FramesToSkip)
+ return 0;
+
+ /* Copy over the stack trace.
+ * At the same time we calculate the stack trace hash by
+ * summing the addresses.
+ */
+ for (i = 0, hash = 0; i < FramesToCapture; i++)
+ {
+ if (FramesToSkip + i >= framesFound)
+ break;
+
+ BackTrace[i] = backTrace[FramesToSkip + i];
+ hash += PtrToUlong(BackTrace[i]);
+ }
+
+ /* Pass the hash back if the caller requested it. */
+ if (BackTraceHash)
+ *BackTraceHash = hash;
+
+ /* Return the number of addresses we copied. */
+ return i;
+}
+
+/* KphCaptureAndAddStack
+ *
+ * Captures a stack trace and adds it to a trace database.
+ */
+BOOLEAN KphCaptureAndAddStack(
+ __in PKPH_TRACE_DATABASE Database,
+ __in KPH_CAPTURE_AND_ADD_STACK_TYPE Type,
+ __out_opt PRTL_TRACE_BLOCK *TraceBlock
+ )
+{
+ return KphpCaptureAndAddStack(
+ Database->Database,
+ Type,
+ TraceBlock
+ );
+}
+
+/* KphCreateTraceDatabase
+ *
+ * Creates a trace database.
+ */
+NTSTATUS KphCreateTraceDatabase(
+ __out PKPH_TRACE_DATABASE *Database,
+ __in_opt SIZE_T MaximumSize,
+ __in ULONG Flags,
+ __in ULONG Tag
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PRTL_TRACE_DATABASE rtlDatabase;
+ PKPH_TRACE_DATABASE database;
+
+ /* Create the trace database. */
+ rtlDatabase = RtlTraceDatabaseCreate(
+ 8,
+ MaximumSize,
+ Flags,
+ Tag,
+ NULL
+ );
+
+ if (!rtlDatabase)
+ return STATUS_INSUFFICIENT_RESOURCES;
+
+ /* Create the object. */
+ status = KphCreateObject(
+ &database,
+ sizeof(KPH_TRACE_DATABASE),
+ 0,
+ KphTraceDatabaseType,
+ 0
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ /* Destroy the trace database, since we can't use it. */
+ RtlTraceDatabaseDestroy(rtlDatabase);
+
+ return status;
+ }
+
+ /* Set up the trace database object. */
+ database->Database = rtlDatabase;
+ *Database = database;
+
+ return status;
+}
+
+NTSTATUS KphQueryTraceDatabase(
+ __in PKPH_TRACE_DATABASE Database,
+ __out_bcount_opt(BufferLength) PKPH_TRACEDB_INFORMATION Buffer,
+ __in_opt ULONG BufferLength,
+ __out_opt PULONG ReturnLength,
+ __in KPROCESSOR_MODE AccessMode
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PRTL_TRACE_DATABASE rtlDatabase = Database->Database;
+ PKPH_TRACEDB_INFORMATION nextEntry;
+ RTL_TRACE_ENUMERATE enumContext = { 0 };
+ PRTL_TRACE_BLOCK currentBlock;
+
+ /* Probe buffers. */
+ if (AccessMode != KernelMode)
+ {
+ __try
+ {
+ if (Buffer)
+ ProbeForWrite(Buffer, BufferLength, 1);
+ if (ReturnLength)
+ ProbeForWrite(ReturnLength, sizeof(ULONG), 1);
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ return GetExceptionCode();
+ }
+ }
+
+ /* First entry to write to. */
+ /* Note that this is completely safe if Buffer is NULL. */
+ nextEntry = Buffer;
+
+ /* Enumerate the trace blocks. */
+ while (RtlTraceDatabaseEnumerate(rtlDatabase, &enumContext, ¤tBlock))
+ {
+ PKPH_TRACEDB_INFORMATION currentEntry;
+
+ /* Save the pointer to the entry we are about to write to. */
+ currentEntry = nextEntry;
+ /* Compute the location of the next entry. */
+ nextEntry = (PKPH_TRACEDB_INFORMATION)(
+ (ULONG_PTR)currentEntry + /* Current entry plus */
+ sizeof(KPH_TRACEDB_INFORMATION) - /* the size of the current entry minus */
+ sizeof(PVOID) + /* the extra PVOID in the Trace array plus */
+ currentBlock->Size * sizeof(PVOID) /* the size of the stack trace. */
+ );
+
+ if (
+ /* If we got an error last time we tried to write to the buffer,
+ * don't try again this time. */
+ NT_SUCCESS(status) &&
+ /* Make sure the buffer isn't NULL. */
+ Buffer &&
+ /* Make sure we don't exceed the buffer length. */
+ ((ULONG_PTR)nextEntry - (ULONG_PTR)Buffer) <= BufferLength
+ )
+ {
+ __try
+ {
+ currentEntry->NextEntryOffset = (ULONG)((ULONG_PTR)nextEntry - (ULONG_PTR)currentEntry);
+ currentEntry->Count = currentBlock->Count;
+ currentEntry->TraceSize = currentBlock->Size;
+ memcpy(currentEntry->Trace, currentBlock->Trace, currentBlock->Size * sizeof(PVOID));
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ status = GetExceptionCode();
+ }
+ }
+ else
+ {
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+ }
+
+ if (ReturnLength)
+ {
+ __try
+ {
+ *ReturnLength = (ULONG)((ULONG_PTR)nextEntry - (ULONG_PTR)Buffer);
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ status = GetExceptionCode();
+ }
+ }
+
+ return status;
+}
+
+/* KphCaptureAndAddStack
+ *
+ * Captures a stack trace and adds it to a trace database.
+ */
+BOOLEAN KphpCaptureAndAddStack(
+ __in PRTL_TRACE_DATABASE Database,
+ __in KPH_CAPTURE_AND_ADD_STACK_TYPE Type,
+ __out_opt PRTL_TRACE_BLOCK *TraceBlock
+ )
+{
+ PVOID trace[MAX_STACK_DEPTH * 2];
+ ULONG kmodeFramesFound = 0;
+ ULONG umodeFramesFound = 0;
+
+ /* Check input. */
+ if (Type >= KphCaptureAndAddMaximum)
+ return FALSE;
+
+ /* Capture the kernel-mode stack if needed. */
+ if (
+ Type == KphCaptureAndAddKModeStack ||
+ Type == KphCaptureAndAddBothStacks
+ )
+ kmodeFramesFound = KphCaptureStackBackTrace(
+ 1,
+ MAX_STACK_DEPTH - 1,
+ 0,
+ trace,
+ NULL
+ );
+ /* Capture the user-mode stack if needed. */
+ if (
+ Type == KphCaptureAndAddUModeStack ||
+ Type == KphCaptureAndAddBothStacks
+ )
+ umodeFramesFound = KphCaptureStackBackTrace(
+ 0,
+ MAX_STACK_DEPTH - 1,
+ RTL_WALK_USER_MODE_STACK,
+ &trace[kmodeFramesFound],
+ NULL
+ );
+
+ /* Add the trace to the database. */
+ return RtlTraceDatabaseAdd(
+ Database,
+ kmodeFramesFound + umodeFramesFound,
+ trace,
+ TraceBlock
+ );
+}
+
+/* KphpTraceDatabaseDeleteProcedure
+ *
+ * Destroys a trace database.
+ */
+VOID KphpTraceDatabaseDeleteProcedure(
+ __in PVOID Object,
+ __in ULONG Flags
+ )
+{
+ PKPH_TRACE_DATABASE database = (PKPH_TRACE_DATABASE)Object;
+
+ RtlTraceDatabaseDestroy(database->Database);
+}
diff --git a/branches/ph-plugins/KProcessHacker/util.c b/branches/ph-plugins/KProcessHacker/util.c
new file mode 100644
index 000000000..61e3b3ecf
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/util.c
@@ -0,0 +1,115 @@
+/*
+ * Process Hacker Driver -
+ * utility functions
+ *
+ * Copyright (C) 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 .
+ */
+
+#include "include/util.h"
+
+/* KphInitializeStream
+ *
+ * Initializes a stream.
+ *
+ * Stream: The stream to initialize.
+ * Buffer: The buffer to use.
+ * Length: The maximum number of bytes that can be stored in
+ * the buffer. If an attempt is made to overrun or underrun
+ * the buffer, an exception will be raised.
+ */
+VOID KphInitializeStream(
+ __out PKPH_STREAM Stream,
+ __in PVOID Buffer,
+ __in ULONG Length
+ )
+{
+ ASSERT(Length > 0);
+
+ Stream->Buffer = Buffer;
+ Stream->Length = Length;
+ Stream->Position = 0;
+}
+
+/* KphSeekStream
+ *
+ * Changes the position of a stream.
+ */
+ULONG KphSeekStream(
+ __inout PKPH_STREAM Stream,
+ __in LONG Offset,
+ __in KPH_STREAM_ORIGIN Origin
+ )
+{
+ ULONG newPosition;
+
+ switch (Origin)
+ {
+ case StartOrigin:
+ {
+ /* Can't seek to before the start of the buffer. */
+ if (Offset < 0)
+ ExRaiseStatus(STATUS_INVALID_PARAMETER_2);
+
+ newPosition = Offset;
+ }
+ break;
+
+ case CurrentOrigin:
+ {
+ newPosition = Stream->Position + Offset;
+ }
+ break;
+
+ case EndOrigin:
+ {
+ newPosition = Stream->Length - Offset - 1;
+ }
+ break;
+ }
+
+ /* Check the new position and raise an exception if
+ * appropriate.
+ */
+ KphCheckStreamPosition(Stream, newPosition);
+ Stream->Position = newPosition;
+
+ return newPosition;
+}
+
+/* KphWriteDataStream
+ *
+ * Writes data to a stream.
+ */
+ULONG KphWriteDataStream(
+ __inout PKPH_STREAM Stream,
+ __in PVOID Data,
+ __in ULONG Length
+ )
+{
+ /* Check if we are going to overrun the buffer. */
+ KphCheckStreamPosition(Stream, Stream->Position + Length);
+ /* Copy the data. */
+ memcpy(
+ PTR_ADD_OFFSET(Stream->Buffer, Stream->Position),
+ Data,
+ Length
+ );
+
+ /* Increase the position. */
+ return Stream->Position += Length;
+}
diff --git a/branches/ph-plugins/KProcessHacker/version.c b/branches/ph-plugins/KProcessHacker/version.c
new file mode 100644
index 000000000..e8a7a8410
--- /dev/null
+++ b/branches/ph-plugins/KProcessHacker/version.c
@@ -0,0 +1,536 @@
+/*
+ * Process Hacker Driver -
+ * Windows version-specific data
+ *
+ * Copyright (C) 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 .
+ */
+
+#define _VERSION_PRIVATE
+#include "include/version.h"
+#include "include/debug.h"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, KvInit)
+#pragma alloc_text(PAGE, KvScanProc)
+#pragma alloc_text(PAGE, KvVerifyPrologue)
+#endif
+
+/*
+ * mov edi, edi
+ * push ebp
+ * mov ebp, esp
+ */
+static char StandardPrologue[] = { 0x8b, 0xff, 0x55, 0x8b, 0xec };
+
+/* KiFastCallEntry */
+/*
+ * Note that this scan will get the address of
+ * mov esi, edx
+ * within KiFastCallEntry, not the start of KiFastCallEntry.
+ * We will then subtract 7 to get the address of
+ * inc dword ptr fs:PbSystemCalls
+ * See sysservice.c for more details.
+ */
+static char KiFastCallEntry51[] =
+{
+ 0x8b, 0xf2, 0x8b, 0x5f, 0x0c, 0x33, 0xc9, 0x8a,
+ 0x0c, 0x18, 0x8b, 0x3f, 0x8b, 0x1c, 0x87, 0x2b
+};
+static char KiFastCallEntry60[] =
+{
+ 0x8b, 0xf2, 0x33, 0xc9, 0x8b, 0x57, 0x0c, 0x8b,
+ 0x3f, 0x8a, 0x0c, 0x10, 0x8b, 0x14, 0x87, 0x2b
+};
+static char KiFastCallEntry61[] =
+{
+ 0x8b, 0xf2, 0x33, 0xc9, 0x8b, 0x57, 0x0c, 0x8b,
+ 0x3f, 0x8a, 0x0c, 0x10, 0x8b, 0x14, 0x87, 0x2b
+};
+/* Below is the scan to find the start of KiFastCallEntry. */
+/* static char KiFastCallEntry[] =
+{
+ 0xb9, 0x23, 0x00, 0x00, 0x00, 0x6a, 0x30, 0x0f,
+ 0xa1, 0x8e, 0xd9, 0x8e, 0xc1, 0x64, 0x8b, 0x0d
+}; */
+
+/* PsExitSpecialApc */
+static char PsExitSpecialApc51[] =
+{
+ 0x8b, 0xff, 0x55, 0x8b, 0xec, 0x64, 0xa1, 0x24,
+ 0x01, 0x00, 0x00, 0x8b, 0x45, 0x08, 0xf6, 0x40
+};
+static char PsExitSpecialApc60[] =
+{
+ 0x8b, 0xff, 0x55, 0x8b, 0xec, 0x83, 0xe4, 0xf8,
+ 0x51, 0x8b, 0x45, 0x08, 0xf6, 0x40, 0x28, 0x01
+};
+static char PsExitSpecialApc61[] =
+{
+ 0x8b, 0xff, 0x55, 0x8b, 0xec, 0x83, 0xe4, 0xf8,
+ 0x51, 0x8b, 0x45, 0x08, 0xf6, 0x40, 0x28, 0x01
+}; /* same as 6.0 */
+
+/* PsTerminateProcess/PspTerminateProcess */
+static char PspTerminateProcess51[] =
+{
+ 0x8b, 0xff, 0x55, 0x8b, 0xec, 0x56, 0x64, 0xa1,
+ 0x24, 0x01, 0x00, 0x00, 0x8b, 0x75, 0x08, 0x3b
+};
+static char PsTerminateProcess60[] =
+{
+ 0x8b, 0xff, 0x55, 0x8b, 0xec, 0x53, 0x56, 0x57,
+ 0x33, 0xd2, 0x6a, 0x08, 0x42, 0x5e, 0x8d, 0xb9
+};
+static char PsTerminateProcess61[] =
+{
+ 0x8b, 0xff, 0x55, 0x8b, 0xec, 0x51, 0x51, 0x53,
+ 0x56, 0x64, 0x8b, 0x35, 0x24, 0x01, 0x00, 0x00,
+ 0x66, 0xff, 0x8e, 0x84, 0x00, 0x00, 0x00, 0x57,
+ 0xc7, 0x45, 0xfc
+}; /* a lot of functions seem to share the first
+ * 16 bytes of the Windows 7 PsTerminateProcess,
+ * and a few even share the first 24 bytes.
+ */
+
+/* PspTerminateThreadByPointer */
+static char PspTerminateThreadByPointer51[] =
+{
+ 0x8b, 0xff, 0x55, 0x8b, 0xec, 0x83, 0xec, 0x0c,
+ 0x83, 0x4d, 0xf8, 0xff, 0x56, 0x57, 0x8b, 0x7d
+};
+static char PspTerminateThreadByPointer60[] =
+{
+ 0x8b, 0xff, 0x55, 0x8b, 0xec, 0x83, 0xe4, 0xf8,
+ 0x51, 0x53, 0x56, 0x8b, 0x75, 0x08, 0x57, 0x8d,
+ 0xbe, 0x60, 0x02, 0x00, 0x00, 0xf6, 0x07, 0x40
+};
+static char PspTerminateThreadByPointer61[] =
+{
+ 0x8b, 0xff, 0x55, 0x8b, 0xec, 0x83, 0xe4, 0xf8,
+ 0x51, 0x53, 0x56, 0x8b, 0x75, 0x08, 0x57, 0x8d,
+ 0xbe, 0x80, 0x02, 0x00, 0x00, 0xf6, 0x07, 0x40
+};
+
+/* The following offsets took me a long time to work out, so
+ please do not steal them. If you want to use them, please
+ license your project under the GNU GPL (although you are
+ not legally required to).
+ */
+NTSTATUS KvInit()
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ ULONG majorVersion, minorVersion, servicePack, buildNumber;
+
+ /* Get Windows version information. */
+
+ RtlWindowsVersion.dwOSVersionInfoSize = sizeof(RtlWindowsVersion);
+ status = RtlGetVersion((PRTL_OSVERSIONINFOW)&RtlWindowsVersion);
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ majorVersion = RtlWindowsVersion.dwMajorVersion;
+ minorVersion = RtlWindowsVersion.dwMinorVersion;
+ servicePack = RtlWindowsVersion.wServicePackMajor;
+ buildNumber = RtlWindowsVersion.dwBuildNumber;
+ dfprintf("Windows %d.%d, SP%d.%d, build %d\n",
+ majorVersion, minorVersion, servicePack,
+ RtlWindowsVersion.wServicePackMinor, buildNumber
+ );
+
+ __NtClose = GetSystemRoutineAddress(L"NtClose");
+
+ /* NtClose is used as a reference point for most addresses
+ dependent on where the kernel is loaded, so if we don't
+ have it, we can't proceed.
+ */
+ if (!__NtClose)
+ return STATUS_NOT_SUPPORTED;
+
+ /* We also need the address of ZwClose to get KiFastCallEntry. */
+ __ZwClose = GetSystemRoutineAddress(L"ZwClose");
+
+ if (!__ZwClose)
+ return STATUS_NOT_SUPPORTED;
+
+ /* Windows XP */
+ if (majorVersion == 5 && minorVersion == 1)
+ {
+ ULONG_PTR searchOffset = (ULONG_PTR)__NtClose;
+
+ WindowsVersion = WINDOWS_XP;
+ ProcessAllAccess = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xfff;
+ ThreadAllAccess = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x3ff;
+
+ OffEtClientId = 0x1ec;
+ OffEtSpareByteForSs = 0x256; /* Padding, last */
+ OffEtStartAddress = 0x224;
+ OffEtWin32StartAddress = 0x228;
+ OffEpJob = 0x134;
+ OffEpObjectTable = 0xc4;
+ OffEpProtectedProcessOff = 0;
+ OffEpProtectedProcessBit = 0;
+ OffEpRundownProtect = 0x80;
+ OffOhBody = 0x18;
+ OffOtName = 0x40;
+ OffOtiGenericMapping = 0x60 + 0x8;
+ OffOtiOpenProcedure = 0x60 + 0x30;
+
+ SsNtContinue = 0x20;
+
+ /* INIT_SCAN(
+ KiFastCallEntryScan,
+ KiFastCallEntry51,
+ sizeof(KiFastCallEntry51),
+ (ULONG_PTR)__ZwClose, SCAN_LENGTH, -6
+ ); */
+ /* We are scanning for PspTerminateProcess which has
+ the same signature as PsTerminateProcess because
+ PsTerminateProcess is simply a wrapper on XP.
+ */
+ INIT_SCAN(
+ PsTerminateProcessScan,
+ PspTerminateProcess51,
+ sizeof(PspTerminateProcess51),
+ searchOffset, SCAN_LENGTH, 0
+ );
+ INIT_SCAN(
+ PspTerminateThreadByPointerScan,
+ PspTerminateThreadByPointer51,
+ sizeof(PspTerminateThreadByPointer51),
+ searchOffset, SCAN_LENGTH, 0
+ );
+
+ /* Windows XP SP0 and 1 are not supported */
+ if (servicePack == 0)
+ {
+ return STATUS_NOT_SUPPORTED;
+ }
+ else if (servicePack == 1)
+ {
+ return STATUS_NOT_SUPPORTED;
+ }
+ else if (servicePack == 2)
+ {
+ }
+ else if (servicePack == 3)
+ {
+ }
+ else
+ {
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ dprintf("Initialized version-specific data for Windows XP SP%d\n", servicePack);
+ }
+ /* Windows Server 2003 */
+ else if (majorVersion == 5 && minorVersion == 2)
+ {
+ WindowsVersion = WINDOWS_SERVER_2003;
+
+ /* Not supported yet */
+ return STATUS_NOT_SUPPORTED;
+ }
+ /* Windows Vista, Windows Server 2008 */
+ else if (majorVersion == 6 && minorVersion == 0)
+ {
+ ULONG_PTR searchOffset = (ULONG_PTR)__NtClose;
+
+ WindowsVersion = WINDOWS_VISTA;
+ ProcessAllAccess = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xffff;
+ ThreadAllAccess = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xffff;
+ OffEtClientId = 0x20c;
+ OffEtSpareByteForSs = 0x26f; /* Padding, second-last */
+ OffEtStartAddress = 0x1f8;
+ OffEtWin32StartAddress = 0x240;
+ OffEpJob = 0x10c;
+ OffEpObjectTable = 0xdc;
+ OffEpProtectedProcessOff = 0x224;
+ OffEpProtectedProcessBit = 0xb;
+ OffEpRundownProtect = 0x98;
+ OffOhBody = 0x18;
+
+ INIT_SCAN(
+ KiFastCallEntryScan,
+ KiFastCallEntry60,
+ sizeof(KiFastCallEntry60),
+ (ULONG_PTR)__ZwClose, SCAN_LENGTH, -7
+ );
+ INIT_SCAN(
+ PsTerminateProcessScan,
+ PsTerminateProcess60,
+ sizeof(PsTerminateProcess60),
+ searchOffset, SCAN_LENGTH, 0
+ );
+ INIT_SCAN(
+ PspTerminateThreadByPointerScan,
+ PspTerminateThreadByPointer60,
+ sizeof(PspTerminateThreadByPointer60),
+ searchOffset - 0x50000, SCAN_LENGTH, 0
+ );
+
+ /* SP0 */
+ if (servicePack == 0)
+ {
+ OffOtName = 0x40;
+ OffOtiGenericMapping = 0x60 + 0xc;
+ OffOtiOpenProcedure = 0x60 + 0x30;
+
+ SsNtContinue = 0x36;
+ }
+ /* SP1 */
+ else if (servicePack == 1)
+ {
+ OffOtName = 0x8;
+ OffOtiGenericMapping = 0x28 + 0xc; /* They got rid of the Mutex (an ERESOURCE) */
+ OffOtiOpenProcedure = 0x28 + 0x34;
+
+ SsNtContinue = 0x37;
+ }
+ /* SP2 */
+ else if (servicePack == 2)
+ {
+ OffOtName = 0x8;
+ OffOtiGenericMapping = 0x28 + 0xc;
+ OffOtiOpenProcedure = 0x28 + 0x34;
+
+ SsNtAddAtom = 0x8;
+ SsNtAlertResumeThread = 0xd;
+ SsNtAlertThread = 0xe;
+ SsNtAllocateLocallyUniqueId = 0xf;
+ SsNtAllocateUserPhysicalPages = 0x10;
+ SsNtAllocateUuids = 0x11;
+ SsNtAllocateVirtualMemory = 0x12;
+ SsNtApphelpCacheControl = 0x28;
+ SsNtAreMappedFilesTheSame = 0x29;
+ SsNtAssignProcessToJobObject = 0x2a;
+ SsNtCallbackReturn = 0x2b;
+ SsNtCancelDeviceWakeupRequest = 0x2c;
+ SsNtCancelIoFile = 0x2d;
+ SsNtCancelTimer = 0x2e;
+ SsNtClearEvent = 0x2f;
+ SsNtClose = 0x30;
+ SsNtContinue = 0x37;
+ SsNtCreateDebugObject = 0x38;
+ SsNtCreateDirectoryObject = 0x39;
+ SsNtCreateEvent = 0x3a;
+ SsNtCreateEventPair = 0x3b;
+ SsNtCreateFile = 0x3c;
+ SsNtCreateIoCompletion = 0x3d;
+ SsNtCreateJobObject = 0x3e;
+ SsNtCreateJobSet = 0x3f;
+ SsNtCreateKey = 0x40;
+ SsNtCreateKeyedEvent = 0x168;
+ SsNtCreateMailslotFile = 0x42;
+ SsNtCreateMutant = 0x43;
+ SsNtCreateNamedPipeFile = 0x44;
+ SsNtCreatePagingFile = 0x46;
+ SsNtCreatePort = 0x47;
+ SsNtCreatePrivateNamespace = 0x45;
+ SsNtCreateProcess = 0x48;
+ SsNtCreateProcessEx = 0x49;
+ SsNtCreateProfile = 0x4a;
+ SsNtCreateSection = 0x4b;
+ SsNtCreateSemaphore = 0x4c;
+ SsNtCreateSymbolicLinkObject = 0x4d;
+ SsNtCreateThread = 0x4e;
+ SsNtCreateTimer = 0x4f;
+ SsNtCreateToken = 0x50;
+ SsNtCreateUserProcess = 0x17f;
+ SsNtCreateWaitablePort = 0x73;
+ SsNtDebugActiveProcess = 0x74;
+ SsNtDebugContinue = 0x75;
+ SsNtDelayExecution = 0x76;
+ SsNtDeleteAtom = 0x77;
+ SsNtDeleteBootEntry = 0x78;
+ SsNtDeleteDriverEntry = 0x79;
+ SsNtDeleteFile = 0x7a;
+ SsNtDeleteKey = 0x7b;
+ SsNtDeletePrivateNamespace = 0x7c;
+ SsNtDeleteObjectAuditAlarm = 0x7d;
+ SsNtDeleteValueKey = 0x7e;
+ SsNtDeviceIoControlFile = 0x7f;
+ SsNtDisplayString = 0x80;
+ SsNtDuplicateObject = 0x81;
+ SsNtDuplicateToken = 0x82;
+ SsNtEnumerateBootEntries = 0x83;
+ SsNtEnumerateDriverEntries = 0x84;
+ SsNtEnumerateKey = 0x85;
+ SsNtEnumerateSystemEnvironmentValuesEx = 0x86;
+ SsNtEnumerateValueKey = 0x88;
+ SsNtExtendSection = 0x89;
+ SsNtFilterToken = 0x8a;
+ SsNtFindAtom = 0x8b;
+ SsNtFlushBuffersFile = 0x8c;
+ SsNtFlushInstructionCache = 0x8d;
+ SsNtFlushKey = 0x8e;
+ SsNtFlushProcessWriteBuffers = 0x8f;
+ SsNtFlushVirtualMemory = 0x90;
+ SsNtFlushWriteBuffer = 0x91;
+ SsNtFreeUserPhysicalPages = 0x92;
+ SsNtFreeVirtualMemory = 0x93;
+ SsNtFsControlFile = 0x96;
+ SsNtGetContextThread = 0x97;
+ SsNtGetDevicePowerState = 0x98;
+ SsNtGetPlugPlayEvent = 0x9a;
+ SsNtGetWriteWatch = 0x9b;
+ SsNtImpersonateAnonymousToken = 0x9c;
+ SsNtImpersonateClientOfPort = 0x9d;
+ SsNtImpersonateThread = 0x9e;
+ SsNtInitiatePowerAction = 0xa1;
+ SsNtIsProcessInJob = 0xa2;
+ SsNtIsSystemResumeAutomatic = 0xa3;
+ SsNtListenPort = 0xa4;
+ SsNtLoadDriver = 0xa5;
+ SsNtLoadKey = 0xa6;
+ SsNtLoadKey2 = 0xa7;
+ SsNtLockFile = 0xa9;
+ SsNtLockVirtualMemory = 0xac;
+ SsNtMakePermanentObject = 0xad;
+ SsNtMakeTemporaryObject = 0xae;
+ SsNtMapUserPhysicalPages = 0xaf;
+ SsNtMapUserPhysicalPagesScatter = 0xb0;
+ SsNtMapViewOfSection = 0xb1;
+ SsNtModifyBootEntry = 0xb2;
+ SsNtModifyDriverEntry = 0xb3;
+ SsNtNotifyChangeDirectoryFile = 0xb4;
+ SsNtNotifyChangeKey = 0xb5;
+ SsNtNotifyChangeMultipleKeys = 0xb6;
+ SsNtOpenDirectoryObject = 0xb7;
+ SsNtOpenEvent = 0xb8;
+ SsNtOpenEventPair = 0xb9;
+ SsNtOpenFile = 0xba;
+ SsNtOpenIoCompletion = 0xbb;
+ SsNtOpenJobObject = 0xbc;
+ SsNtOpenKey = 0xbd;
+ SsNtOpenKeyedEvent = 0x169;
+ SsNtOpenMutant = 0xbf;
+ SsNtOpenObjectAuditAlarm = 0xc1;
+ SsNtOpenProcess = 0xc2;
+ SsNtOpenProcessToken = 0xc3;
+ SsNtOpenProcessTokenEx = 0xc4;
+ SsNtOpenSection = 0xc5;
+ SsNtOpenSemaphore = 0xc6;
+ SsNtOpenSymbolicLinkObject = 0xc8;
+ SsNtOpenThread = 0xc9;
+ SsNtOpenThreadToken = 0xca;
+ SsNtOpenThreadTokenEx = 0xcb;
+ SsNtOpenTimer = 0xcc;
+ SsNtReadFile = 0x102;
+ SsNtWriteFile = 0x163;
+ }
+ else
+ {
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ dprintf("Initialized version-specific data for Windows Vista SP%d/Windows Server 2008\n", servicePack);
+ }
+ /* Windows 7 */
+ else if (majorVersion == 6 && minorVersion == 1)
+ {
+ ULONG_PTR psSearchOffset = (ULONG_PTR)GetSystemRoutineAddress(L"PsSetCreateProcessNotifyRoutine");
+ ULONG psScanLength = 0x200000;
+
+ if (!psSearchOffset)
+ return STATUS_NOT_SUPPORTED;
+
+ WindowsVersion = WINDOWS_7;
+ ProcessAllAccess = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xffff;
+ ThreadAllAccess = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xffff;
+ OffEtClientId = 0x22c;
+ OffEtSpareByteForSs = 0x2b4; /* Padding, last */
+ OffEtStartAddress = 0x218;
+ OffEtWin32StartAddress = 0x260;
+ OffEpJob = 0x124;
+ OffEpObjectTable = 0xf4;
+ OffEpProtectedProcessOff = 0x26c;
+ OffEpProtectedProcessBit = 0xb;
+ OffEpRundownProtect = 0xb0;
+ OffOhBody = 0x18;
+ OffOtName = 0x8;
+ OffOtiGenericMapping = 0x28 + 0xc;
+ OffOtiOpenProcedure = 0x28 + 0x34;
+
+ SsNtContinue = 0x3c;
+
+ INIT_SCAN(
+ KiFastCallEntryScan,
+ KiFastCallEntry61,
+ sizeof(KiFastCallEntry61),
+ (ULONG_PTR)__ZwClose, SCAN_LENGTH, -7
+ );
+ INIT_SCAN(
+ PsTerminateProcessScan,
+ PsTerminateProcess61,
+ sizeof(PsTerminateProcess61),
+ psSearchOffset, psScanLength, 0
+ );
+ INIT_SCAN(
+ PspTerminateThreadByPointerScan,
+ PspTerminateThreadByPointer61,
+ sizeof(PspTerminateThreadByPointer61),
+ psSearchOffset, psScanLength, 0
+ );
+
+ /* SP0 */
+ if (servicePack == 0)
+ {
+ }
+ else
+ {
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ dprintf("Initialized version-specific data for Windows 7 SP%d\n", servicePack);
+ }
+ else
+ {
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ return status;
+}
+
+PVOID KvScanProc(
+ PKV_SCANPROC ScanProc
+ )
+{
+ PUCHAR bytes = ScanProc->Bytes;
+ ULONG length = ScanProc->Length;
+ ULONG_PTR endAddress = ScanProc->StartAddress + ScanProc->ScanLength;
+ ULONG_PTR i;
+
+ for (i = ScanProc->StartAddress; i < endAddress; i++)
+ {
+ if (memcmp((PVOID)i, bytes, length) == 0)
+ return (PVOID)(i + ScanProc->Displacement);
+ }
+
+ return NULL;
+}
+
+PVOID KvVerifyPrologue(
+ PVOID Address
+ )
+{
+ if (memcmp(Address, StandardPrologue, 5) == 0)
+ return Address;
+ else
+ return NULL;
+}
diff --git a/branches/ph-plugins/LICENSE.txt b/branches/ph-plugins/LICENSE.txt
new file mode 100644
index 000000000..89c3eac73
--- /dev/null
+++ b/branches/ph-plugins/LICENSE.txt
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program 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.
+
+ This program 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 this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/branches/ph-plugins/NProcessHacker/NProcessHacker.sln b/branches/ph-plugins/NProcessHacker/NProcessHacker.sln
new file mode 100644
index 000000000..e43c98b54
--- /dev/null
+++ b/branches/ph-plugins/NProcessHacker/NProcessHacker.sln
@@ -0,0 +1,52 @@
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NProcessHacker", "NProcessHacker.vcproj", "{52426135-4597-4988-B5BC-BE8E93489A53}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test", "Test\Test.vcproj", "{A910A5C9-9BCA-47BA-AFAD-92698E512DED}"
+ ProjectSection(ProjectDependencies) = postProject
+ {52426135-4597-4988-B5BC-BE8E93489A53} = {52426135-4597-4988-B5BC-BE8E93489A53}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NProcessHackerHook", "NProcessHackerHook\NProcessHackerHook.vcproj", "{DC2D13B6-4BD1-4AC8-933F-DDB0DE850162}"
+ ProjectSection(ProjectDependencies) = postProject
+ {52426135-4597-4988-B5BC-BE8E93489A53} = {52426135-4597-4988-B5BC-BE8E93489A53}
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Debug|Win64 = Debug|Win64
+ Release|Win32 = Release|Win32
+ Release|Win64 = Release|Win64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {52426135-4597-4988-B5BC-BE8E93489A53}.Debug|Win32.ActiveCfg = Debug|x64
+ {52426135-4597-4988-B5BC-BE8E93489A53}.Debug|Win32.Build.0 = Debug|x64
+ {52426135-4597-4988-B5BC-BE8E93489A53}.Debug|Win64.ActiveCfg = Debug|x64
+ {52426135-4597-4988-B5BC-BE8E93489A53}.Debug|Win64.Build.0 = Debug|x64
+ {52426135-4597-4988-B5BC-BE8E93489A53}.Release|Win32.ActiveCfg = Release|Win32
+ {52426135-4597-4988-B5BC-BE8E93489A53}.Release|Win32.Build.0 = Release|Win32
+ {52426135-4597-4988-B5BC-BE8E93489A53}.Release|Win64.ActiveCfg = Release|x64
+ {52426135-4597-4988-B5BC-BE8E93489A53}.Release|Win64.Build.0 = Release|x64
+ {A910A5C9-9BCA-47BA-AFAD-92698E512DED}.Debug|Win32.ActiveCfg = Debug|Win32
+ {A910A5C9-9BCA-47BA-AFAD-92698E512DED}.Debug|Win32.Build.0 = Debug|Win32
+ {A910A5C9-9BCA-47BA-AFAD-92698E512DED}.Debug|Win64.ActiveCfg = Debug|x64
+ {A910A5C9-9BCA-47BA-AFAD-92698E512DED}.Debug|Win64.Build.0 = Debug|x64
+ {A910A5C9-9BCA-47BA-AFAD-92698E512DED}.Release|Win32.ActiveCfg = Release|Win32
+ {A910A5C9-9BCA-47BA-AFAD-92698E512DED}.Release|Win32.Build.0 = Release|Win32
+ {A910A5C9-9BCA-47BA-AFAD-92698E512DED}.Release|Win64.ActiveCfg = Release|x64
+ {A910A5C9-9BCA-47BA-AFAD-92698E512DED}.Release|Win64.Build.0 = Release|x64
+ {DC2D13B6-4BD1-4AC8-933F-DDB0DE850162}.Debug|Win32.ActiveCfg = Debug|x64
+ {DC2D13B6-4BD1-4AC8-933F-DDB0DE850162}.Debug|Win32.Build.0 = Debug|x64
+ {DC2D13B6-4BD1-4AC8-933F-DDB0DE850162}.Debug|Win64.ActiveCfg = Debug|x64
+ {DC2D13B6-4BD1-4AC8-933F-DDB0DE850162}.Debug|Win64.Build.0 = Debug|x64
+ {DC2D13B6-4BD1-4AC8-933F-DDB0DE850162}.Release|Win32.ActiveCfg = Release|Win32
+ {DC2D13B6-4BD1-4AC8-933F-DDB0DE850162}.Release|Win32.Build.0 = Release|Win32
+ {DC2D13B6-4BD1-4AC8-933F-DDB0DE850162}.Release|Win64.ActiveCfg = Release|x64
+ {DC2D13B6-4BD1-4AC8-933F-DDB0DE850162}.Release|Win64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/branches/ph-plugins/NProcessHacker/NProcessHacker.vcproj b/branches/ph-plugins/NProcessHacker/NProcessHacker.vcproj
new file mode 100644
index 000000000..6834dca3a
--- /dev/null
+++ b/branches/ph-plugins/NProcessHacker/NProcessHacker.vcproj
@@ -0,0 +1,410 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/branches/ph-plugins/NProcessHacker/NProcessHackerHook/NProcessHackerHook.vcproj b/branches/ph-plugins/NProcessHacker/NProcessHackerHook/NProcessHackerHook.vcproj
new file mode 100644
index 000000000..254e1902d
--- /dev/null
+++ b/branches/ph-plugins/NProcessHacker/NProcessHackerHook/NProcessHackerHook.vcproj
@@ -0,0 +1,334 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/branches/ph-plugins/NProcessHacker/NProcessHackerHook/nphhook.c b/branches/ph-plugins/NProcessHacker/NProcessHackerHook/nphhook.c
new file mode 100644
index 000000000..c0d06ae3a
--- /dev/null
+++ b/branches/ph-plugins/NProcessHacker/NProcessHackerHook/nphhook.c
@@ -0,0 +1,23 @@
+#include
+#include "../kphhook.h"
+
+BOOL WINAPI DllMain(
+ HINSTANCE hinstDLL,
+ DWORD fdwReason,
+ LPVOID lpvReserved
+ )
+{
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ KphHookInit();
+ break;
+ case DLL_PROCESS_DETACH:
+ KphHookDeinit();
+ break;
+ default:
+ break;
+ }
+
+ return TRUE;
+}
diff --git a/branches/ph-plugins/NProcessHacker/Release/NProcessHacker.dll b/branches/ph-plugins/NProcessHacker/Release/NProcessHacker.dll
new file mode 100644
index 000000000..b37a9623d
Binary files /dev/null and b/branches/ph-plugins/NProcessHacker/Release/NProcessHacker.dll differ
diff --git a/branches/ph-plugins/NProcessHacker/Test/Test.vcproj b/branches/ph-plugins/NProcessHacker/Test/Test.vcproj
new file mode 100644
index 000000000..fd7dceb32
--- /dev/null
+++ b/branches/ph-plugins/NProcessHacker/Test/Test.vcproj
@@ -0,0 +1,343 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/branches/ph-plugins/NProcessHacker/Test/test.c b/branches/ph-plugins/NProcessHacker/Test/test.c
new file mode 100644
index 000000000..33abb2b12
--- /dev/null
+++ b/branches/ph-plugins/NProcessHacker/Test/test.c
@@ -0,0 +1,29 @@
+#ifndef UNICODE
+#define UNICODE
+#endif
+
+#include
+#include
+#include "../kph.h"
+#include "../kphhook.h"
+
+int wmain(int argc, WCHAR *argv[])
+{
+ ULONG pid;
+ HANDLE processHandle;
+ CHAR memory[0x1000];
+
+ if (argc < 2)
+ {
+ printf("Usage: test [pid to kill]\n");
+ return 1;
+ }
+
+ pid = _wtoi(argv[1]);
+
+ KphHookInit();
+ processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
+ ReadProcessMemory(processHandle, (PVOID)0x10000, memory, 0x1000, NULL);
+
+ return 0;
+}
diff --git a/branches/ph-plugins/NProcessHacker/hook.c b/branches/ph-plugins/NProcessHacker/hook.c
new file mode 100644
index 000000000..0ba0a55ed
--- /dev/null
+++ b/branches/ph-plugins/NProcessHacker/hook.c
@@ -0,0 +1,96 @@
+/*
+ * Process Hacker Library -
+ * hooks
+ *
+ * Copyright (C) 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 .
+ */
+
+#include "hook.h"
+
+VOID PHAPI PhInitializeHook(
+ PPH_HOOK Hook,
+ PVOID Function,
+ PVOID Target
+ )
+{
+ memset(Hook, 0, sizeof(PH_HOOK));
+ Hook->Function = Function;
+ Hook->Target = Target;
+}
+
+NTSTATUS PHAPI PhHook(
+ PPH_HOOK Hook
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ ULONG oldProtection;
+ PCHAR function;
+
+ /* Change the page protection of the target page so we can write to it. */
+ if (!VirtualProtect(Hook->Function, 5, PAGE_EXECUTE_READWRITE, &oldProtection))
+ return STATUS_ACCESS_VIOLATION;
+
+ __try
+ {
+ function = (PCHAR)Hook->Function;
+ /* Copy the original five bytes for unhooking. */
+ memcpy(Hook->Bytes, function, 5);
+ /* Hook the function by writing a jump instruction. */
+ Hook->Hooked = TRUE;
+ /* jmp Target */
+ *function = 0xe9;
+ *(PULONG_PTR)(function + 1) = (ULONG_PTR)Hook->Target - (ULONG_PTR)Hook->Function - 5;
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ status = GetExceptionCode();
+ }
+
+ /* Restore the old page protection. */
+ VirtualProtect(Hook->Function, 5, oldProtection, NULL);
+
+ return status;
+}
+
+NTSTATUS PHAPI PhUnhook(
+ PPH_HOOK Hook
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ ULONG oldProtection;
+
+ /* Change the page protection of the target page so we can write to it. */
+ if (!VirtualProtect(Hook->Function, 5, PAGE_EXECUTE_READWRITE, &oldProtection))
+ return STATUS_ACCESS_VIOLATION;
+
+ __try
+ {
+ /* Unpatch the function by restoring the original first 5 bytes. */
+ memcpy(Hook->Function, Hook->Bytes, 5);
+ Hook->Hooked = FALSE;
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ status = GetExceptionCode();
+ }
+
+ /* Restore the old page protection. */
+ VirtualProtect(Hook->Function, 5, oldProtection, NULL);
+
+ return status;
+}
diff --git a/branches/ph-plugins/NProcessHacker/hook.h b/branches/ph-plugins/NProcessHacker/hook.h
new file mode 100644
index 000000000..e06c74bfc
--- /dev/null
+++ b/branches/ph-plugins/NProcessHacker/hook.h
@@ -0,0 +1,93 @@
+/*
+ * Process Hacker Library -
+ * hooks
+ *
+ * Copyright (C) 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 .
+ */
+
+#ifndef _HOOK_H
+#define _HOOK_H
+
+#include "nph.h"
+
+/* Almost exactly the same as the hooking code in KProcessHacker. */
+
+#ifdef _X86_
+
+#define PH_DEFINE_HOOK_CALL(Name, Arguments, Hook) \
+ __declspec(naked) Name(Arguments) \
+ { \
+ __asm lea eax, Hook \
+ __asm mov eax, [eax+PH_HOOK.Function] \
+ __asm add eax, 5 \
+ __asm push ebp \
+ __asm mov ebp, esp \
+ __asm jmp eax \
+ } \
+
+#define PH_DEFINE_NT_HOOK_CALL(Name, Arguments, Hook) \
+ __declspec(naked) Name(Arguments) \
+ { \
+ __asm lea eax, Hook \
+ __asm mov edx, [eax+PH_HOOK.Function] \
+ __asm add edx, 5 \
+ /* Store the system call number in eax. */ \
+ __asm mov eax, dword ptr [eax+PH_HOOK.Bytes+1] \
+ __asm jmp edx \
+ } \
+
+#else
+
+#define PH_DEFINE_HOOK_CALL(Name, Arguments, Hook) \
+ Name(Arguments) \
+ { \
+ RaiseException(STATUS_NOT_SUPPORTED, 0, 0, NULL); \
+ return 0; \
+ } \
+
+#define PH_DEFINE_NT_HOOK_CALL(Name, Arguments, Hook) \
+ Name(Arguments) \
+ { \
+ RaiseException(STATUS_NOT_SUPPORTED, 0, 0, NULL); \
+ return 0; \
+ } \
+
+#endif
+typedef struct _PH_HOOK
+{
+ PVOID Function;
+ PVOID Target;
+ BOOLEAN Hooked;
+ CHAR Bytes[5];
+} PH_HOOK, *PPH_HOOK;
+
+NPHAPI VOID PHAPI PhInitializeHook(
+ PPH_HOOK Hook,
+ PVOID Function,
+ PVOID Target
+ );
+
+NPHAPI NTSTATUS PHAPI PhHook(
+ PPH_HOOK Hook
+ );
+
+NPHAPI NTSTATUS PHAPI PhUnhook(
+ PPH_HOOK Hook
+ );
+
+#endif
diff --git a/branches/ph-plugins/NProcessHacker/kph.c b/branches/ph-plugins/NProcessHacker/kph.c
new file mode 100644
index 000000000..3f5052c26
--- /dev/null
+++ b/branches/ph-plugins/NProcessHacker/kph.c
@@ -0,0 +1,880 @@
+/*
+ * Process Hacker Library -
+ * KProcessHacker interface
+ *
+ * Copyright (C) 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 .
+ */
+
+#include "kph.h"
+
+NTSTATUS PHAPI KphpDeviceIoControl(
+ HANDLE KphHandle,
+ ULONG KphControlCode,
+ PVOID InBuffer,
+ ULONG InBufferLength,
+ PVOID OutBuffer,
+ ULONG OutBufferLength,
+ PULONG ReturnLength
+ );
+
+_NtDeviceIoControlFile NtDeviceIoControlFile = NULL;
+_NtTerminateProcess NtTerminateProcess = NULL;
+_NtTerminateThread NtTerminateThread = NULL;
+
+NTSTATUS PHAPI KphInit()
+{
+ if (!(NtDeviceIoControlFile = (_NtDeviceIoControlFile)
+ PhGetProcAddress(L"ntdll.dll", "NtDeviceIoControlFile")))
+ return STATUS_PROCEDURE_NOT_FOUND;
+ if (!(NtTerminateProcess = (_NtTerminateProcess)
+ PhGetProcAddress(L"ntdll.dll", "NtTerminateProcess")))
+ return STATUS_PROCEDURE_NOT_FOUND;
+ if (!(NtTerminateThread = (_NtTerminateThread)
+ PhGetProcAddress(L"ntdll.dll", "NtTerminateThread")))
+ return STATUS_PROCEDURE_NOT_FOUND;
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS PHAPI KphConnect(
+ __out PHANDLE KphHandle
+ )
+{
+ HANDLE deviceHandle;
+
+ deviceHandle = CreateFileW(
+ KPH_DEVICE_NAME,
+ FILE_GENERIC_READ | FILE_GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL
+ );
+
+ if (deviceHandle == INVALID_HANDLE_VALUE)
+ {
+ deviceHandle = NULL;
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ *KphHandle = deviceHandle;
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS PHAPI KphDisconnect(
+ __in HANDLE KphHandle
+ )
+{
+ if (CloseHandle(KphHandle))
+ return STATUS_SUCCESS;
+ else
+ return STATUS_INVALID_HANDLE;
+}
+
+NTSTATUS PHAPI KphpDeviceIoControl(
+ HANDLE KphHandle,
+ ULONG KphControlCode,
+ PVOID InBuffer,
+ ULONG InBufferLength,
+ PVOID OutBuffer,
+ ULONG OutBufferLength,
+ PULONG ReturnLength
+ )
+{
+ NTSTATUS status;
+ IO_STATUS_BLOCK ioStatusBlock;
+
+ status = NtDeviceIoControlFile(
+ KphHandle,
+ NULL,
+ NULL,
+ NULL,
+ &ioStatusBlock,
+ KphControlCode,
+ InBuffer,
+ InBufferLength,
+ OutBuffer,
+ OutBufferLength
+ );
+
+ if (NT_SUCCESS(status) && ReturnLength)
+ *ReturnLength = ioStatusBlock.Information;
+
+ return status;
+}
+
+NTSTATUS PHAPI KphGetFeatures(
+ __in HANDLE KphHandle,
+ __out PULONG Features
+ )
+{
+ NTSTATUS status;
+ ULONG features;
+
+ if (NT_SUCCESS(
+ status = KphpDeviceIoControl(
+ KphHandle,
+ KPH_GETFEATURES,
+ NULL,
+ 0,
+ &features,
+ sizeof(ULONG),
+ NULL)
+ ))
+ *Features = features;
+
+ return status;
+}
+
+NTSTATUS PHAPI KphRead(
+ __in HANDLE KphHandle,
+ __in PVOID Address,
+ __out_bcount(BufferLength) PVOID Buffer,
+ __in ULONG BufferLength
+ )
+{
+ return KphpDeviceIoControl(
+ KphHandle,
+ KPH_READ,
+ &Address,
+ sizeof(PVOID),
+ Buffer,
+ BufferLength,
+ NULL
+ );
+}
+
+NTSTATUS PHAPI KphWrite(
+ __in HANDLE KphHandle,
+ __in PVOID Address,
+ __in_bcount(Length) PVOID Buffer,
+ __in ULONG Length
+ )
+{
+ NTSTATUS status;
+ PVOID data = PhAlloc(Length + sizeof(PVOID));
+
+ *(PVOID *)data = Address;
+ memcpy((PCHAR)data + sizeof(PVOID), Buffer, Length);
+
+ status = KphpDeviceIoControl(
+ KphHandle,
+ KPH_WRITE,
+ data,
+ Length + sizeof(PVOID),
+ NULL,
+ 0,
+ NULL
+ );
+ PhFree(data);
+
+ return status;
+}
+
+NTSTATUS PHAPI KphOpenProcess(
+ __in HANDLE KphHandle,
+ __out PHANDLE ProcessHandle,
+ __in HANDLE ProcessId,
+ __in ACCESS_MASK DesiredAccess
+ )
+{
+ NTSTATUS status;
+
+ struct
+ {
+ HANDLE ProcessId;
+ ACCESS_MASK DesiredAccess;
+ } args;
+ struct
+ {
+ HANDLE ProcessHandle;
+ } ret;
+
+ args.ProcessId = ProcessId;
+ args.DesiredAccess = DesiredAccess;
+
+ status = KphpDeviceIoControl(
+ KphHandle,
+ KPH_OPENPROCESS,
+ &args,
+ sizeof(args),
+ &ret,
+ sizeof(ret),
+ NULL
+ );
+
+ *ProcessHandle = ret.ProcessHandle;
+
+ return status;
+}
+
+NTSTATUS PHAPI KphOpenThread(
+ __in HANDLE KphHandle,
+ __out PHANDLE ThreadHandle,
+ __in HANDLE ThreadId,
+ __in ACCESS_MASK DesiredAccess
+ )
+{
+ NTSTATUS status;
+
+ struct
+ {
+ HANDLE ThreadId;
+ ACCESS_MASK DesiredAccess;
+ } args;
+ struct
+ {
+ HANDLE ThreadHandle;
+ } ret;
+
+ args.ThreadId = ThreadId;
+ args.DesiredAccess = DesiredAccess;
+
+ status = KphpDeviceIoControl(
+ KphHandle,
+ KPH_OPENTHREAD,
+ &args,
+ sizeof(args),
+ &ret,
+ sizeof(ret),
+ NULL
+ );
+
+ *ThreadHandle = ret.ThreadHandle;
+
+ return status;
+}
+
+NTSTATUS PHAPI KphOpenProcessToken(
+ __in HANDLE KphHandle,
+ __out PHANDLE TokenHandle,
+ __in HANDLE ProcessHandle,
+ __in ACCESS_MASK DesiredAccess
+ )
+{
+ NTSTATUS status;
+
+ struct
+ {
+ HANDLE ProcessHandle;
+ ACCESS_MASK DesiredAccess;
+ } args;
+ struct
+ {
+ HANDLE TokenHandle;
+ } ret;
+
+ args.ProcessHandle = ProcessHandle;
+ args.DesiredAccess = DesiredAccess;
+
+ status = KphpDeviceIoControl(
+ KphHandle,
+ KPH_OPENPROCESSTOKEN,
+ &args,
+ sizeof(args),
+ &ret,
+ sizeof(ret),
+ NULL
+ );
+
+ *TokenHandle = ret.TokenHandle;
+
+ return status;
+}
+
+NTSTATUS PHAPI KphGetProcessProtected(
+ __in HANDLE KphHandle,
+ __in ULONG_PTR ProcessId,
+ __out PBOOLEAN IsProtected
+ )
+{
+ NTSTATUS status;
+
+ struct
+ {
+ HANDLE ProcessId;
+ } args;
+ struct
+ {
+ BOOLEAN IsProtected;
+ } ret;
+
+ args.ProcessId = (HANDLE)ProcessId;
+
+ status = KphpDeviceIoControl(
+ KphHandle,
+ KPH_GETPROCESSPROTECTED,
+ &args,
+ sizeof(args),
+ &ret,
+ sizeof(ret),
+ NULL
+ );
+
+ *IsProtected = ret.IsProtected;
+
+ return status;
+}
+
+NTSTATUS PHAPI KphSetProcessProtected(
+ __in HANDLE KphHandle,
+ __in ULONG_PTR ProcessId,
+ __in BOOLEAN IsProtected
+ )
+{
+ struct
+ {
+ HANDLE ProcessId;
+ BOOLEAN IsProtected;
+ } args;
+
+ args.ProcessId = (HANDLE)ProcessId;
+ args.IsProtected = IsProtected;
+
+ return KphpDeviceIoControl(
+ KphHandle,
+ KPH_SETPROCESSPROTECTED,
+ &args,
+ sizeof(args),
+ NULL,
+ 0,
+ NULL
+ );
+}
+
+NTSTATUS PHAPI KphTerminateProcess(
+ __in HANDLE KphHandle,
+ __in HANDLE ProcessHandle,
+ __in NTSTATUS ExitStatus
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ struct
+ {
+ HANDLE ProcessHandle;
+ NTSTATUS ExitStatus;
+ } args;
+
+ args.ProcessHandle = ProcessHandle;
+ args.ExitStatus = ExitStatus;
+
+ status = KphpDeviceIoControl(
+ KphHandle,
+ KPH_TERMINATEPROCESS,
+ &args,
+ sizeof(args),
+ NULL,
+ 0,
+ NULL
+ );
+
+ /* Check if we were trying to terminate the current
+ * process and do it now. */
+ if (status == STATUS_CANT_TERMINATE_SELF)
+ status = NtTerminateProcess(GetCurrentProcess(), ExitStatus);
+
+ return status;
+}
+
+NTSTATUS PHAPI KphSuspendProcess(
+ __in HANDLE KphHandle,
+ __in HANDLE ProcessHandle
+ )
+{
+ struct
+ {
+ HANDLE ProcessHandle;
+ } args;
+
+ args.ProcessHandle = ProcessHandle;
+
+ return KphpDeviceIoControl(
+ KphHandle,
+ KPH_SUSPENDPROCESS,
+ &args,
+ sizeof(args),
+ NULL,
+ 0,
+ NULL
+ );
+}
+
+NTSTATUS PHAPI KphResumeProcess(
+ __in HANDLE KphHandle,
+ __in HANDLE ProcessHandle
+ )
+{
+ struct
+ {
+ HANDLE ProcessHandle;
+ } args;
+
+ args.ProcessHandle = ProcessHandle;
+
+ return KphpDeviceIoControl(
+ KphHandle,
+ KPH_RESUMEPROCESS,
+ &args,
+ sizeof(args),
+ NULL,
+ 0,
+ NULL
+ );
+}
+
+NTSTATUS PHAPI KphReadVirtualMemory(
+ __in HANDLE KphHandle,
+ __in HANDLE ProcessHandle,
+ __in PVOID BaseAddress,
+ __out_bcount(BufferLength) PVOID Buffer,
+ __in ULONG BufferLength,
+ __out_opt PULONG ReturnLength
+ )
+{
+ struct
+ {
+ HANDLE ProcessHandle;
+ PVOID BaseAddress;
+ PVOID Buffer;
+ ULONG BufferLength;
+ PULONG ReturnLength;
+ } args;
+
+ args.ProcessHandle = ProcessHandle;
+ args.BaseAddress = BaseAddress;
+ args.Buffer = Buffer;
+ args.BufferLength = BufferLength;
+ args.ReturnLength = ReturnLength;
+
+ return KphpDeviceIoControl(
+ KphHandle,
+ KPH_READVIRTUALMEMORY,
+ &args,
+ sizeof(args),
+ NULL,
+ 0,
+ NULL
+ );
+}
+
+NTSTATUS PHAPI KphWriteVirtualMemory(
+ __in HANDLE KphHandle,
+ __in HANDLE ProcessHandle,
+ __in PVOID BaseAddress,
+ __in_bcount(BufferLength) PVOID Buffer,
+ __in ULONG BufferLength,
+ __out_opt PULONG ReturnLength
+ )
+{
+ struct
+ {
+ HANDLE ProcessHandle;
+ PVOID BaseAddress;
+ PVOID Buffer;
+ ULONG BufferLength;
+ PULONG ReturnLength;
+ } args;
+
+ args.ProcessHandle = ProcessHandle;
+ args.BaseAddress = BaseAddress;
+ args.Buffer = Buffer;
+ args.BufferLength = BufferLength;
+ args.ReturnLength = ReturnLength;
+
+ return KphpDeviceIoControl(
+ KphHandle,
+ KPH_WRITEVIRTUALMEMORY,
+ &args,
+ sizeof(args),
+ NULL,
+ 0,
+ NULL
+ );
+}
+
+NTSTATUS PHAPI KphOpenProcessJob(
+ __in HANDLE KphHandle,
+ __out PHANDLE JobHandle,
+ __in HANDLE ProcessHandle,
+ __in ACCESS_MASK DesiredAccess
+ )
+{
+ NTSTATUS status;
+
+ struct
+ {
+ HANDLE ProcessHandle;
+ ACCESS_MASK DesiredAccess;
+ } args;
+ struct
+ {
+ HANDLE JobHandle;
+ } ret;
+
+ args.ProcessHandle = ProcessHandle;
+ args.DesiredAccess = DesiredAccess;
+
+ status = KphpDeviceIoControl(
+ KphHandle,
+ KPH_OPENPROCESSJOB,
+ &args,
+ sizeof(args),
+ &ret,
+ sizeof(ret),
+ NULL
+ );
+
+ *JobHandle = ret.JobHandle;
+
+ return status;
+}
+
+NTSTATUS PHAPI KphGetContextThread(
+ __in HANDLE KphHandle,
+ __in HANDLE ThreadHandle,
+ __inout PCONTEXT ThreadContext
+ )
+{
+ struct
+ {
+ HANDLE ThreadHandle;
+ PCONTEXT ThreadContext;
+ } args;
+
+ args.ThreadHandle = ThreadHandle;
+ args.ThreadContext = ThreadContext;
+
+ return KphpDeviceIoControl(
+ KphHandle,
+ KPH_GETCONTEXTTHREAD,
+ &args,
+ sizeof(args),
+ NULL,
+ 0,
+ NULL
+ );
+}
+
+NTSTATUS PHAPI KphSetContextThread(
+ __in HANDLE KphHandle,
+ __in HANDLE ThreadHandle,
+ __in PCONTEXT ThreadContext
+ )
+{
+ struct
+ {
+ HANDLE ThreadHandle;
+ PCONTEXT ThreadContext;
+ } args;
+
+ args.ThreadHandle = ThreadHandle;
+ args.ThreadContext = ThreadContext;
+
+ return KphpDeviceIoControl(
+ KphHandle,
+ KPH_SETCONTEXTTHREAD,
+ &args,
+ sizeof(args),
+ NULL,
+ 0,
+ NULL
+ );
+}
+
+NTSTATUS PHAPI KphTerminateThread(
+ __in HANDLE KphHandle,
+ __in HANDLE ThreadHandle,
+ __in NTSTATUS ExitStatus
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ struct
+ {
+ HANDLE ThreadHandle;
+ NTSTATUS ExitStatus;
+ } args;
+
+ args.ThreadHandle = ThreadHandle;
+ args.ExitStatus = ExitStatus;
+
+ status = KphpDeviceIoControl(
+ KphHandle,
+ KPH_TERMINATETHREAD,
+ &args,
+ sizeof(args),
+ NULL,
+ 0,
+ NULL
+ );
+
+ if (status == STATUS_CANT_TERMINATE_SELF)
+ status = NtTerminateThread(GetCurrentThread(), ExitStatus);
+
+ return status;
+}
+
+NTSTATUS PHAPI KphSetHandleGrantedAccess(
+ __in HANDLE KphHandle,
+ __in HANDLE Handle,
+ __in ACCESS_MASK GrantedAccess
+ )
+{
+ struct
+ {
+ HANDLE Handle;
+ ACCESS_MASK GrantedAccess;
+ } args;
+
+ args.Handle = Handle;
+ args.GrantedAccess = GrantedAccess;
+
+ return KphpDeviceIoControl(
+ KphHandle,
+ KPH_SETHANDLEGRANTEDACCESS,
+ &args,
+ sizeof(args),
+ NULL,
+ 0,
+ NULL
+ );
+}
+
+NTSTATUS PHAPI KphProtectAdd(
+ __in HANDLE KphHandle,
+ __in HANDLE ProcessHandle,
+ __in BOOLEAN AllowKernelMode,
+ __in ACCESS_MASK ProcessAllowMask,
+ __in ACCESS_MASK ThreadAllowMask
+ )
+{
+ struct
+ {
+ HANDLE ProcessHandle;
+ LOGICAL AllowKernelMode;
+ ACCESS_MASK ProcessAllowMask;
+ ACCESS_MASK ThreadAllowMask;
+ } args;
+
+ args.ProcessHandle = ProcessHandle;
+ args.AllowKernelMode = AllowKernelMode;
+ args.ProcessAllowMask = ProcessAllowMask;
+ args.ThreadAllowMask = ThreadAllowMask;
+
+ return KphpDeviceIoControl(
+ KphHandle,
+ KPH_PROTECTADD,
+ &args,
+ sizeof(args),
+ NULL,
+ 0,
+ NULL
+ );
+}
+
+NTSTATUS PHAPI KphProtectRemove(
+ __in HANDLE KphHandle,
+ __in HANDLE ProcessHandle
+ )
+{
+ struct
+ {
+ HANDLE ProcessHandle;
+ } args;
+
+ args.ProcessHandle = ProcessHandle;
+
+ return KphpDeviceIoControl(
+ KphHandle,
+ KPH_PROTECTREMOVE,
+ &args,
+ sizeof(args),
+ NULL,
+ 0,
+ NULL
+ );
+}
+
+NTSTATUS PHAPI KphProtectQuery(
+ __in HANDLE KphHandle,
+ __in HANDLE ProcessHandle,
+ __out PBOOLEAN AllowKernelMode,
+ __out PACCESS_MASK ProcessAllowMask,
+ __out PACCESS_MASK ThreadAllowMask
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ struct
+ {
+ HANDLE ProcessHandle;
+ PLOGICAL AllowKernelMode;
+ PACCESS_MASK ProcessAllowMask;
+ PACCESS_MASK ThreadAllowMask;
+ } args;
+ LOGICAL allowKernelMode;
+
+ args.ProcessHandle = ProcessHandle;
+ args.AllowKernelMode = &allowKernelMode;
+ args.ProcessAllowMask = ProcessAllowMask;
+ args.ThreadAllowMask = ThreadAllowMask;
+
+ status = KphpDeviceIoControl(
+ KphHandle,
+ KPH_PROTECTQUERY,
+ &args,
+ sizeof(args),
+ NULL,
+ 0,
+ NULL
+ );
+
+ *AllowKernelMode = (BOOLEAN)allowKernelMode;
+
+ return status;
+}
+
+NTSTATUS PHAPI KphUnsafeReadVirtualMemory(
+ __in HANDLE KphHandle,
+ __in HANDLE ProcessHandle,
+ __in PVOID BaseAddress,
+ __in_bcount(BufferLength) PVOID Buffer,
+ __in ULONG BufferLength,
+ __out_opt PULONG ReturnLength
+ )
+{
+ struct
+ {
+ HANDLE ProcessHandle;
+ PVOID BaseAddress;
+ PVOID Buffer;
+ ULONG BufferLength;
+ PULONG ReturnLength;
+ } args;
+
+ args.ProcessHandle = ProcessHandle;
+ args.BaseAddress = BaseAddress;
+ args.Buffer = Buffer;
+ args.BufferLength = BufferLength;
+ args.ReturnLength = ReturnLength;
+
+ return KphpDeviceIoControl(
+ KphHandle,
+ KPH_UNSAFEREADVIRTUALMEMORY,
+ &args,
+ sizeof(args),
+ NULL,
+ 0,
+ NULL
+ );
+}
+
+NTSTATUS PHAPI KphSetExecuteOptions(
+ __in HANDLE KphHandle,
+ __in HANDLE ProcessHandle,
+ __in ULONG ExecuteOptions
+ )
+{
+ struct
+ {
+ HANDLE ProcessHandle;
+ ULONG ExecuteOptions;
+ } args;
+
+ args.ProcessHandle = ProcessHandle;
+ args.ExecuteOptions = ExecuteOptions;
+
+ return KphpDeviceIoControl(
+ KphHandle,
+ KPH_SETEXECUTEOPTIONS,
+ &args,
+ sizeof(args),
+ NULL,
+ 0,
+ NULL
+ );
+}
+
+NTSTATUS PHAPI KphQueryProcessHandles(
+ __in HANDLE KphHandle,
+ __in HANDLE ProcessHandle,
+ __out_bcount_opt(BufferLength) PVOID Buffer,
+ __in_opt ULONG BufferLength,
+ __out_opt PULONG ReturnLength
+ )
+{
+ struct
+ {
+ HANDLE ProcessHandle;
+ PVOID Buffer;
+ ULONG BufferLength;
+ PULONG ReturnLength;
+ } args;
+
+ args.ProcessHandle = ProcessHandle;
+ args.Buffer = Buffer;
+ args.BufferLength = BufferLength;
+ args.ReturnLength = ReturnLength;
+
+ return KphpDeviceIoControl(
+ KphHandle,
+ KPH_QUERYPROCESSHANDLES,
+ &args,
+ sizeof(args),
+ NULL,
+ 0,
+ NULL
+ );
+}
+
+NTSTATUS PHAPI KphOpenThreadProcess(
+ __in HANDLE KphHandle,
+ __out PHANDLE ProcessHandle,
+ __in HANDLE ThreadHandle,
+ __in ACCESS_MASK DesiredAccess
+ )
+{
+ NTSTATUS status;
+
+ struct
+ {
+ HANDLE ThreadHandle;
+ ACCESS_MASK DesiredAccess;
+ } args;
+ struct
+ {
+ HANDLE ProcessHandle;
+ } ret;
+
+ args.ThreadHandle = ThreadHandle;
+ args.DesiredAccess = DesiredAccess;
+
+ status = KphpDeviceIoControl(
+ KphHandle,
+ KPH_OPENTHREADPROCESS,
+ &args,
+ sizeof(args),
+ &ret,
+ sizeof(ret),
+ NULL
+ );
+
+ *ProcessHandle = ret.ProcessHandle;
+
+ return status;
+}
diff --git a/branches/ph-plugins/NProcessHacker/kph.h b/branches/ph-plugins/NProcessHacker/kph.h
new file mode 100644
index 000000000..f66820022
--- /dev/null
+++ b/branches/ph-plugins/NProcessHacker/kph.h
@@ -0,0 +1,289 @@
+/*
+ * Process Hacker Library -
+ * KProcessHacker interface
+ *
+ * Copyright (C) 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 .
+ */
+
+#ifndef _KPH_H
+#define _KPH_H
+
+#include "nph.h"
+#include "nativedefs.h"
+
+#define KPH_DEVICE_TYPE (0x9999)
+#define KPH_DEVICE_NAME (L"\\\\.\\KProcessHacker")
+
+#define KPHF_PSTERMINATEPROCESS 0x1
+#define KPHF_PSPTERMINATETHREADBPYPOINTER 0x2
+
+#define METHOD_BUFFERED 0
+#define METHOD_IN_DIRECT 1
+#define METHOD_OUT_DIRECT 2
+#define METHOD_NEITHER 3
+
+#ifndef FILE_ANY_ACCESS
+#define FILE_ANY_ACCESS 0
+#define FILE_SPECIAL_ACCESS (FILE_ANY_ACCESS)
+#define FILE_READ_ACCESS (0x0001)
+#define FILE_WRITE_ACCESS (0x0002)
+#endif
+
+#ifndef CTL_CODE
+#define CTL_CODE(DeviceType, Function, Method, Access) ( \
+ ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
+#define KPH_CTL_CODE(x) CTL_CODE(KPH_DEVICE_TYPE, 0x800 + x, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#endif
+
+#define KPH_READ KPH_CTL_CODE(0)
+#define KPH_WRITE KPH_CTL_CODE(1)
+#define KPH_GETFILEOBJECTNAME KPH_CTL_CODE(2)
+#define KPH_OPENPROCESS KPH_CTL_CODE(3)
+#define KPH_OPENTHREAD KPH_CTL_CODE(4)
+#define KPH_OPENPROCESSTOKEN KPH_CTL_CODE(5)
+#define KPH_GETPROCESSPROTECTED KPH_CTL_CODE(6)
+#define KPH_SETPROCESSPROTECTED KPH_CTL_CODE(7)
+#define KPH_TERMINATEPROCESS KPH_CTL_CODE(8)
+#define KPH_SUSPENDPROCESS KPH_CTL_CODE(9)
+#define KPH_RESUMEPROCESS KPH_CTL_CODE(10)
+#define KPH_READVIRTUALMEMORY KPH_CTL_CODE(11)
+#define KPH_WRITEVIRTUALMEMORY KPH_CTL_CODE(12)
+#define KPH_SETPROCESSTOKEN KPH_CTL_CODE(13)
+#define KPH_GETTHREADSTARTADDRESS KPH_CTL_CODE(14)
+#define KPH_SETHANDLEATTRIBUTES KPH_CTL_CODE(15)
+#define KPH_GETHANDLEOBJECTNAME KPH_CTL_CODE(16)
+#define KPH_OPENPROCESSJOB KPH_CTL_CODE(17)
+#define KPH_GETCONTEXTTHREAD KPH_CTL_CODE(18)
+#define KPH_SETCONTEXTTHREAD KPH_CTL_CODE(19)
+#define KPH_GETTHREADWIN32THREAD KPH_CTL_CODE(20)
+#define KPH_DUPLICATEOBJECT KPH_CTL_CODE(21)
+#define KPH_ZWQUERYOBJECT KPH_CTL_CODE(22)
+#define KPH_GETPROCESSID KPH_CTL_CODE(23)
+#define KPH_GETTHREADID KPH_CTL_CODE(24)
+#define KPH_TERMINATETHREAD KPH_CTL_CODE(25)
+#define KPH_GETFEATURES KPH_CTL_CODE(26)
+#define KPH_SETHANDLEGRANTEDACCESS KPH_CTL_CODE(27)
+#define KPH_ASSIGNIMPERSONATIONTOKEN KPH_CTL_CODE(28)
+#define KPH_PROTECTADD KPH_CTL_CODE(29)
+#define KPH_PROTECTREMOVE KPH_CTL_CODE(30)
+#define KPH_PROTECTQUERY KPH_CTL_CODE(31)
+#define KPH_UNSAFEREADVIRTUALMEMORY KPH_CTL_CODE(32)
+#define KPH_SETEXECUTEOPTIONS KPH_CTL_CODE(33)
+#define KPH_QUERYPROCESSHANDLES KPH_CTL_CODE(34)
+#define KPH_OPENTHREADPROCESS KPH_CTL_CODE(35)
+
+#ifndef MEM_EXECUTE_OPTION_DISABLE
+#define MEM_EXECUTE_OPTION_DISABLE 0x1
+#define MEM_EXECUTE_OPTION_ENABLE 0x2
+#define MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION 0x4
+#define MEM_EXECUTE_OPTION_PERMANENT 0x8
+#endif
+
+typedef struct _PROCESS_HANDLE
+{
+ HANDLE Handle;
+ PVOID Object;
+ ACCESS_MASK GrantedAccess;
+ ULONG HandleAttributes;
+} PROCESS_HANDLE, *PPROCESS_HANDLE;
+
+typedef struct _PROCESS_HANDLE_INFORMATION
+{
+ ULONG HandleCount;
+ PROCESS_HANDLE Handles[1];
+} PROCESS_HANDLE_INFORMATION, *PPROCESS_HANDLE_INFORMATION;
+
+NTSTATUS PHAPI KphInit();
+
+NPHAPI NTSTATUS PHAPI KphConnect(
+ __out PHANDLE KphHandle
+ );
+
+NPHAPI NTSTATUS PHAPI KphDisconnect(
+ __in HANDLE KphHandle
+ );
+
+NPHAPI NTSTATUS PHAPI KphGetFeatures(
+ __in HANDLE KphHandle,
+ __out PULONG Features
+ );
+
+NPHAPI NTSTATUS PHAPI KphRead(
+ __in HANDLE KphHandle,
+ __in PVOID Address,
+ __out_bcount(BufferLength) PVOID Buffer,
+ __in ULONG BufferLength
+ );
+
+NPHAPI NTSTATUS PHAPI KphWrite(
+ __in HANDLE KphHandle,
+ __in PVOID Address,
+ __in_bcount(Length) PVOID Buffer,
+ __in ULONG Length
+ );
+
+NPHAPI NTSTATUS PHAPI KphOpenProcess(
+ __in HANDLE KphHandle,
+ __out PHANDLE ProcessHandle,
+ __in HANDLE ProcessId,
+ __in ACCESS_MASK DesiredAccess
+ );
+
+NPHAPI NTSTATUS PHAPI KphOpenThread(
+ __in HANDLE KphHandle,
+ __out PHANDLE ThreadHandle,
+ __in HANDLE ThreadId,
+ __in ACCESS_MASK DesiredAccess
+ );
+
+NPHAPI NTSTATUS PHAPI KphOpenProcessToken(
+ __in HANDLE KphHandle,
+ __out PHANDLE TokenHandle,
+ __in HANDLE ProcessHandle,
+ __in ACCESS_MASK DesiredAccess
+ );
+
+NPHAPI NTSTATUS PHAPI KphGetProcessProtected(
+ __in HANDLE KphHandle,
+ __in ULONG_PTR ProcessId,
+ __out PBOOLEAN IsProtected
+ );
+
+NPHAPI NTSTATUS PHAPI KphSetProcessProtected(
+ __in HANDLE KphHandle,
+ __in ULONG_PTR ProcessId,
+ __in BOOLEAN IsProtected
+ );
+
+NPHAPI NTSTATUS PHAPI KphTerminateProcess(
+ __in HANDLE KphHandle,
+ __in HANDLE ProcessHandle,
+ __in NTSTATUS ExitStatus
+ );
+
+NPHAPI NTSTATUS PHAPI KphSuspendProcess(
+ __in HANDLE KphHandle,
+ __in HANDLE ProcessHandle
+ );
+
+NPHAPI NTSTATUS PHAPI KphResumeProcess(
+ __in HANDLE KphHandle,
+ __in HANDLE ProcessHandle
+ );
+
+NPHAPI NTSTATUS PHAPI KphReadVirtualMemory(
+ __in HANDLE KphHandle,
+ __in HANDLE ProcessHandle,
+ __in PVOID BaseAddress,
+ __out_bcount(BufferLength) PVOID Buffer,
+ __in ULONG BufferLength,
+ __out_opt PULONG ReturnLength
+ );
+
+NPHAPI NTSTATUS PHAPI KphWriteVirtualMemory(
+ __in HANDLE KphHandle,
+ __in HANDLE ProcessHandle,
+ __in PVOID BaseAddress,
+ __in_bcount(BufferLength) PVOID Buffer,
+ __in ULONG BufferLength,
+ __out_opt PULONG ReturnLength
+ );
+
+NPHAPI NTSTATUS PHAPI KphOpenProcessJob(
+ __in HANDLE KphHandle,
+ __out PHANDLE JobHandle,
+ __in HANDLE ProcessHandle,
+ __in ACCESS_MASK DesiredAccess
+ );
+
+NPHAPI NTSTATUS PHAPI KphGetContextThread(
+ __in HANDLE KphHandle,
+ __in HANDLE ThreadHandle,
+ __inout PCONTEXT ThreadContext
+ );
+
+NPHAPI NTSTATUS PHAPI KphSetContextThread(
+ __in HANDLE KphHandle,
+ __in HANDLE ThreadHandle,
+ __in PCONTEXT ThreadContext
+ );
+
+NPHAPI NTSTATUS PHAPI KphTerminateThread(
+ __in HANDLE KphHandle,
+ __in HANDLE ThreadHandle,
+ __in NTSTATUS ExitStatus
+ );
+
+NPHAPI NTSTATUS PHAPI KphSetHandleGrantedAccess(
+ __in HANDLE KphHandle,
+ __in HANDLE Handle,
+ __in ACCESS_MASK GrantedAccess
+ );
+
+NPHAPI NTSTATUS PHAPI KphProtectAdd(
+ __in HANDLE KphHandle,
+ __in HANDLE ProcessHandle,
+ __in BOOLEAN AllowKernelMode,
+ __in ACCESS_MASK ProcessAllowMask,
+ __in ACCESS_MASK ThreadAllowMask
+ );
+
+NPHAPI NTSTATUS PHAPI KphProtectRemove(
+ __in HANDLE KphHandle,
+ __in HANDLE ProcessHandle
+ );
+
+NPHAPI NTSTATUS PHAPI KphProtectQuery(
+ __in HANDLE KphHandle,
+ __in HANDLE ProcessHandle,
+ __out PBOOLEAN AllowKernelMode,
+ __out PACCESS_MASK ProcessAllowMask,
+ __out PACCESS_MASK ThreadAllowMask
+ );
+
+NPHAPI NTSTATUS PHAPI KphUnsafeReadVirtualMemory(
+ __in HANDLE KphHandle,
+ __in HANDLE ProcessHandle,
+ __in PVOID BaseAddress,
+ __in_bcount(BufferLength) PVOID Buffer,
+ __in ULONG BufferLength,
+ __out_opt PULONG ReturnLength
+ );
+
+NPHAPI NTSTATUS PHAPI KphSetExecuteOptions(
+ __in HANDLE KphHandle,
+ __in HANDLE ProcessHandle,
+ __in ULONG ExecuteOptions
+ );
+
+NPHAPI NTSTATUS PHAPI KphQueryProcessHandles(
+ __in HANDLE KphHandle,
+ __in HANDLE ProcessHandle,
+ __out_bcount_opt(BufferLength) PVOID Buffer,
+ __in_opt ULONG BufferLength,
+ __out_opt PULONG ReturnLength
+ );
+
+NPHAPI NTSTATUS PHAPI KphOpenThreadProcess(
+ __in HANDLE KphHandle,
+ __out PHANDLE ProcessHandle,
+ __in HANDLE ThreadHandle,
+ __in ACCESS_MASK DesiredAccess
+ );
+
+#endif
diff --git a/branches/ph-plugins/NProcessHacker/kphhook.c b/branches/ph-plugins/NProcessHacker/kphhook.c
new file mode 100644
index 000000000..243bb5f3a
--- /dev/null
+++ b/branches/ph-plugins/NProcessHacker/kphhook.c
@@ -0,0 +1,180 @@
+/*
+ * Process Hacker Library -
+ * KProcessHacker transparency hooking
+ *
+ * Copyright (C) 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 .
+ */
+
+#include "kphhook.h"
+
+#define STD_PREFIX NTSTATUS NTAPI
+#define DECLARE_NT_HOOK(Name, Arguments) \
+ static _##Name Name; \
+ static PH_HOOK Name##Hook; \
+ PH_DEFINE_NT_HOOK_CALL(NTSTATUS NTAPI Old##Name, Arguments, Name##Hook); \
+ STD_PREFIX New##Name(Arguments)
+#define DECLARE_NEW_FUNC(Name, Arguments) \
+ STD_PREFIX New##Name(Arguments)
+#define INITIALIZE_NT_HOOK(Name) \
+ Name = PhGetProcAddress(L"ntdll.dll", #Name); \
+ PhInitializeHook(&Name##Hook, Name, New##Name); \
+ PhHook(&Name##Hook)
+#define DEINITIALIZE_NT_HOOK(Name) \
+ PhUnhook(&Name##Hook)
+
+BOOLEAN KphHookInitialized = FALSE;
+HANDLE KphHandle = NULL;
+DECLARE_NT_HOOK(NtGetContextThread, NTGETCONTEXTTHREAD_ARGS);
+DECLARE_NT_HOOK(NtOpenProcess, NTOPENPROCESS_ARGS);
+DECLARE_NT_HOOK(NtOpenProcessToken, NTOPENPROCESSTOKEN_ARGS);
+DECLARE_NT_HOOK(NtOpenProcessTokenEx, NTOPENPROCESSTOKENEX_ARGS);
+DECLARE_NT_HOOK(NtOpenThread, NTOPENTHREAD_ARGS);
+DECLARE_NT_HOOK(NtReadVirtualMemory, NTREADVIRTUALMEMORY_ARGS);
+DECLARE_NT_HOOK(NtSetContextThread, NTSETCONTEXTTHREAD_ARGS);
+DECLARE_NT_HOOK(NtTerminateProcess, NTTERMINATEPROCESS_ARGS);
+DECLARE_NT_HOOK(NtTerminateThread, NTTERMINATETHREAD_ARGS);
+DECLARE_NT_HOOK(NtWriteVirtualMemory, NTWRITEVIRTUALMEMORY_ARGS);
+
+VOID PHAPI KphHookInit()
+{
+ if (KphHookInitialized)
+ return;
+
+ if (!NT_SUCCESS(KphConnect(&KphHandle)))
+ return;
+
+ INITIALIZE_NT_HOOK(NtGetContextThread);
+ INITIALIZE_NT_HOOK(NtOpenProcess);
+ INITIALIZE_NT_HOOK(NtOpenProcessToken);
+ INITIALIZE_NT_HOOK(NtOpenProcessTokenEx);
+ INITIALIZE_NT_HOOK(NtOpenThread);
+ INITIALIZE_NT_HOOK(NtReadVirtualMemory);
+ INITIALIZE_NT_HOOK(NtSetContextThread);
+ INITIALIZE_NT_HOOK(NtTerminateProcess);
+ INITIALIZE_NT_HOOK(NtTerminateThread);
+ INITIALIZE_NT_HOOK(NtWriteVirtualMemory);
+
+ KphHookInitialized = TRUE;
+}
+
+VOID PHAPI KphHookDeinit()
+{
+ if (!KphHookInitialized)
+ return;
+
+ DEINITIALIZE_NT_HOOK(NtGetContextThread);
+ DEINITIALIZE_NT_HOOK(NtOpenProcess);
+ DEINITIALIZE_NT_HOOK(NtOpenProcessToken);
+ DEINITIALIZE_NT_HOOK(NtOpenProcessTokenEx);
+ DEINITIALIZE_NT_HOOK(NtOpenThread);
+ DEINITIALIZE_NT_HOOK(NtReadVirtualMemory);
+ DEINITIALIZE_NT_HOOK(NtSetContextThread);
+ DEINITIALIZE_NT_HOOK(NtTerminateProcess);
+ DEINITIALIZE_NT_HOOK(NtTerminateThread);
+ DEINITIALIZE_NT_HOOK(NtWriteVirtualMemory);
+
+ KphDisconnect(KphHandle);
+
+ KphHookInitialized = FALSE;
+}
+
+DECLARE_NEW_FUNC(NtGetContextThread, NTGETCONTEXTTHREAD_ARGS)
+{
+ return KphGetContextThread(KphHandle, ThreadHandle, Context);
+}
+
+DECLARE_NEW_FUNC(NtOpenProcess, NTOPENPROCESS_ARGS)
+{
+ /* Use KPH if we only have a PID, no name or TID. */
+ if (!ObjectAttributes->ObjectName && ClientId->UniqueThread == 0)
+ return KphOpenProcess(KphHandle, ProcessHandle, ClientId->UniqueProcess, DesiredAccess);
+
+ return OldNtOpenProcess(ProcessHandle, DesiredAccess, ObjectAttributes, ClientId);
+}
+
+DECLARE_NEW_FUNC(NtOpenProcessToken, NTOPENPROCESSTOKEN_ARGS)
+{
+ return NtOpenProcessTokenEx(ProcessHandle, DesiredAccess, 0, TokenHandle);
+}
+
+DECLARE_NEW_FUNC(NtOpenProcessTokenEx, NTOPENPROCESSTOKENEX_ARGS)
+{
+ /* HandleAttributes is ignored. */
+ return KphOpenProcessToken(KphHandle, TokenHandle, ProcessHandle, DesiredAccess);
+}
+
+DECLARE_NEW_FUNC(NtOpenThread, NTOPENTHREAD_ARGS)
+{
+ /* Use KPH if we only have a CID, no name. */
+ if (!ObjectAttributes->ObjectName)
+ return KphOpenThread(KphHandle, ThreadHandle, ClientId->UniqueThread, DesiredAccess);
+
+ return OldNtOpenThread(ThreadHandle, DesiredAccess, ObjectAttributes, ClientId);
+}
+
+DECLARE_NEW_FUNC(NtReadVirtualMemory, NTREADVIRTUALMEMORY_ARGS)
+{
+ return KphReadVirtualMemory(KphHandle, ProcessHandle, BaseAddress, Buffer, BufferLength, ReturnLength);
+}
+
+DECLARE_NEW_FUNC(NtSetContextThread, NTSETCONTEXTTHREAD_ARGS)
+{
+ return KphSetContextThread(KphHandle, ThreadHandle, Context);
+}
+
+DECLARE_NEW_FUNC(NtTerminateProcess, NTTERMINATEPROCESS_ARGS)
+{
+ NTSTATUS status;
+
+ /* Call the original NtTerminateProcess if we are terminating self to
+ * avoid infinite recursion with KphTerminateProcess.
+ */
+ if (ProcessHandle == NULL || ProcessHandle == GetCurrentProcess())
+ return OldNtTerminateProcess(ProcessHandle, ExitStatus);
+
+ status = KphTerminateProcess(KphHandle, ProcessHandle, ExitStatus);
+
+ /* Fall back to using the original NtTerminateProcess if KPH couldn't
+ * do it. */
+ if (status == STATUS_NOT_SUPPORTED)
+ status = OldNtTerminateProcess(ProcessHandle, ExitStatus);
+
+ return status;
+}
+
+DECLARE_NEW_FUNC(NtTerminateThread, NTTERMINATETHREAD_ARGS)
+{
+ NTSTATUS status;
+
+ if (ThreadHandle == NULL || ThreadHandle == GetCurrentThread())
+ return OldNtTerminateThread(ThreadHandle, ExitStatus);
+
+ status = KphTerminateThread(KphHandle, ThreadHandle, ExitStatus);
+
+ /* Fall back to using the original NtTerminateThread if KPH couldn't
+ * do it. */
+ if (status == STATUS_NOT_SUPPORTED)
+ status = OldNtTerminateThread(ThreadHandle, ExitStatus);
+
+ return status;
+}
+
+DECLARE_NEW_FUNC(NtWriteVirtualMemory, NTWRITEVIRTUALMEMORY_ARGS)
+{
+ return KphWriteVirtualMemory(KphHandle, ProcessHandle, BaseAddress, Buffer, BufferLength, ReturnLength);
+}
diff --git a/branches/ph-plugins/NProcessHacker/kphhook.h b/branches/ph-plugins/NProcessHacker/kphhook.h
new file mode 100644
index 000000000..ac602c4f3
--- /dev/null
+++ b/branches/ph-plugins/NProcessHacker/kphhook.h
@@ -0,0 +1,32 @@
+/*
+ * Process Hacker Library -
+ * KProcessHacker transparency hooking
+ *
+ * Copyright (C) 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 .
+ */
+
+#ifndef _KPHHOOK_H
+#define _KPHHOOK_H
+
+#include "hook.h"
+#include "kph.h"
+
+NPHAPI VOID PHAPI KphHookInit();
+NPHAPI VOID PHAPI KphHookDeinit();
+
+#endif
diff --git a/branches/ph-plugins/NProcessHacker/nativedefs.h b/branches/ph-plugins/NProcessHacker/nativedefs.h
new file mode 100644
index 000000000..af754046d
--- /dev/null
+++ b/branches/ph-plugins/NProcessHacker/nativedefs.h
@@ -0,0 +1,175 @@
+#ifndef _NATIVEDEFS_H
+#define _NATIVEDEFS_H
+
+#include "nph.h"
+
+typedef enum _OBJECT_INFORMATION_CLASS
+{
+ ObjectBasicInformation,
+ ObjectNameInformation,
+ ObjectTypeInformation,
+ ObjectAllInformation,
+ ObjectDataInformation
+} OBJECT_INFORMATION_CLASS, *POBJECT_INFORMATION_CLASS;
+
+typedef struct _UNICODE_STRING UNICODE_STRING, *PUNICODE_STRING;
+
+typedef struct _CLIENT_ID
+{
+ HANDLE UniqueProcess;
+ HANDLE UniqueThread;
+} CLIENT_ID, *PCLIENT_ID;
+
+typedef struct _IO_STATUS_BLOCK
+{
+ union
+ {
+ NTSTATUS Status;
+ PVOID Pointer;
+ };
+ ULONG_PTR Information;
+} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
+
+typedef struct _OBJECT_ATTRIBUTES
+{
+ ULONG Length;
+ PVOID RootDirectory;
+ PUNICODE_STRING ObjectName;
+ ULONG Attributes;
+ PVOID SecurityDescriptor;
+ PVOID SecurityQualityOfService;
+} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;
+
+typedef struct _UNICODE_STRING
+{
+ USHORT Length;
+ USHORT MaximumLength;
+ PWSTR Buffer;
+} UNICODE_STRING, *PUNICODE_STRING;
+
+typedef struct _OBJECT_NAME_INFORMATION
+{
+ UNICODE_STRING Name;
+} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;
+
+#define NTDEVICEIOCONTROLFILE_ARGS \
+ HANDLE FileHandle, \
+ HANDLE Event, \
+ PVOID ApcRoutine, \
+ PVOID ApcContext, \
+ PIO_STATUS_BLOCK IoStatusBlock, \
+ ULONG IoControlCode, \
+ PVOID InputBuffer, \
+ ULONG InputBufferLength, \
+ PVOID OutputBuffer, \
+ ULONG OutputBufferLength
+
+typedef NTSTATUS (NTAPI *_NtDeviceIoControlFile)(
+ NTDEVICEIOCONTROLFILE_ARGS
+ );
+
+#define NTGETCONTEXTTHREAD_ARGS \
+ HANDLE ThreadHandle, \
+ PCONTEXT Context
+
+typedef NTSTATUS (NTAPI *_NtGetContextThread)(
+ NTGETCONTEXTTHREAD_ARGS
+ );
+
+#define NTOPENPROCESS_ARGS \
+ PHANDLE ProcessHandle, \
+ ACCESS_MASK DesiredAccess, \
+ POBJECT_ATTRIBUTES ObjectAttributes, \
+ PCLIENT_ID ClientId
+
+typedef NTSTATUS (NTAPI *_NtOpenProcess)(
+ NTOPENPROCESS_ARGS
+ );
+
+#define NTOPENPROCESSTOKEN_ARGS \
+ HANDLE ProcessHandle, \
+ ACCESS_MASK DesiredAccess, \
+ PHANDLE TokenHandle
+
+typedef NTSTATUS (NTAPI *_NtOpenProcessToken)(
+ NTOPENPROCESSTOKEN_ARGS
+ );
+
+#define NTOPENPROCESSTOKENEX_ARGS \
+ HANDLE ProcessHandle, \
+ ACCESS_MASK DesiredAccess, \
+ ULONG HandleAttributes, \
+ PHANDLE TokenHandle
+
+typedef NTSTATUS (NTAPI *_NtOpenProcessTokenEx)(
+ NTOPENPROCESSTOKENEX_ARGS
+ );
+
+#define NTOPENTHREAD_ARGS \
+ PHANDLE ThreadHandle, \
+ ACCESS_MASK DesiredAccess, \
+ POBJECT_ATTRIBUTES ObjectAttributes, \
+ PCLIENT_ID ClientId
+
+typedef NTSTATUS (NTAPI *_NtOpenThread)(
+ NTOPENTHREAD_ARGS
+ );
+
+#define NTQUERYOBJECT_ARGS \
+ HANDLE Handle, \
+ OBJECT_INFORMATION_CLASS ObjectInformationClass, \
+ PVOID ObjectInformation, \
+ ULONG Length, \
+ PULONG ReturnLength
+
+typedef NTSTATUS (NTAPI *_NtQueryObject)(
+ NTQUERYOBJECT_ARGS
+ );
+
+#define NTREADVIRTUALMEMORY_ARGS \
+ HANDLE ProcessHandle, \
+ PVOID BaseAddress, \
+ PVOID Buffer, \
+ ULONG BufferLength, \
+ PULONG ReturnLength
+
+typedef NTSTATUS (NTAPI *_NtReadVirtualMemory)(
+ NTREADVIRTUALMEMORY_ARGS
+ );
+
+#define NTSETCONTEXTTHREAD_ARGS \
+ HANDLE ThreadHandle, \
+ PCONTEXT Context
+
+typedef NTSTATUS (NTAPI *_NtSetContextThread)(
+ NTSETCONTEXTTHREAD_ARGS
+ );
+
+#define NTTERMINATEPROCESS_ARGS \
+ HANDLE ProcessHandle, \
+ NTSTATUS ExitStatus
+
+typedef NTSTATUS (NTAPI *_NtTerminateProcess)(
+ NTTERMINATEPROCESS_ARGS
+ );
+
+#define NTTERMINATETHREAD_ARGS \
+ HANDLE ThreadHandle, \
+ NTSTATUS ExitStatus
+
+typedef NTSTATUS (NTAPI *_NtTerminateThread)(
+ NTTERMINATETHREAD_ARGS
+ );
+
+#define NTWRITEVIRTUALMEMORY_ARGS \
+ HANDLE ProcessHandle, \
+ PVOID BaseAddress, \
+ PVOID Buffer, \
+ ULONG BufferLength, \
+ PULONG ReturnLength
+
+typedef NTSTATUS (NTAPI *_NtWriteVirtualMemory)(
+ NTWRITEVIRTUALMEMORY_ARGS
+ );
+
+#endif
\ No newline at end of file
diff --git a/branches/ph-plugins/NProcessHacker/nph.c b/branches/ph-plugins/NProcessHacker/nph.c
new file mode 100644
index 000000000..42c4eeab7
--- /dev/null
+++ b/branches/ph-plugins/NProcessHacker/nph.c
@@ -0,0 +1,85 @@
+/*
+ * Process Hacker Library -
+ * common code
+ *
+ * Copyright (C) 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 .
+ */
+
+#include "nph.h"
+#include "kph.h"
+#include "obj.h"
+#include "verify.h"
+
+PVOID PHAPI PhAlloc(SIZE_T Size)
+{
+ PVOID memory;
+
+ if (!(memory = malloc(Size)))
+ RaiseException(EXCEPTION_NO_MEMORY, 0, 0, NULL);
+
+ return memory;
+}
+
+PVOID PHAPI PhRealloc(PVOID Memory, SIZE_T Size)
+{
+ PVOID memory;
+
+ if (!(memory = realloc(Memory, Size)))
+ RaiseException(EXCEPTION_NO_MEMORY, 0, 0, NULL);
+
+ return memory;
+}
+
+VOID PHAPI PhFree(PVOID Memory)
+{
+ free(Memory);
+}
+
+PVOID PHAPI PhGetProcAddress(PWSTR LibraryName, PSTR ProcName)
+{
+ return GetProcAddress(GetModuleHandle(LibraryName), ProcName);
+}
+
+VOID PHAPI PhVoid()
+{
+ return;
+}
+
+BOOL WINAPI DllMain(
+ HINSTANCE hinstDLL,
+ DWORD fdwReason,
+ LPVOID lpvReserved
+ )
+{
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ if (!NT_SUCCESS(PhVerifyInit()))
+ return FALSE;
+ if (!NT_SUCCESS(PhObjInit()))
+ return FALSE;
+ if (!NT_SUCCESS(KphInit()))
+ return FALSE;
+
+ break;
+ default:
+ break;
+ }
+
+ return TRUE;
+}
diff --git a/branches/ph-plugins/NProcessHacker/nph.h b/branches/ph-plugins/NProcessHacker/nph.h
new file mode 100644
index 000000000..887b94f9f
--- /dev/null
+++ b/branches/ph-plugins/NProcessHacker/nph.h
@@ -0,0 +1,70 @@
+/*
+ * Process Hacker Library -
+ * main header file
+ *
+ * Copyright (C) 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 .
+ */
+
+#ifndef _NPH_H
+#define _NPH_H
+
+/* If the user has already included windows.h, don't include ntstatus.h
+ * to avoid duplicate macro definitions. */
+#ifndef _WINDOWS_
+#include
+#endif
+
+#define WIN32_LEAN_AND_MEAN
+#define WIN32_NO_STATUS /* Need ntstatus.h instead */
+#include
+#include
+
+#ifndef LOGICAL
+#define LOGICAL ULONG
+#define PLOGICAL PULONG
+#endif
+
+#ifndef STATUS_SUCCESS
+#define STATUS_SUCCESS (0)
+#endif
+
+#ifndef NTSTATUS
+#define NTSTATUS LONG
+#endif
+
+#ifndef NT_SUCCESS
+#define NT_SUCCESS(x) ((x) >= STATUS_SUCCESS)
+#endif
+
+#ifdef NPH_EXPORTS
+#define NPHAPI __declspec(dllexport)
+#else
+#define NPHAPI __declspec(dllimport)
+#endif
+
+#define PHAPI __stdcall
+
+#define EXCEPTION_NO_MEMORY STATUS_NO_MEMORY
+
+NPHAPI PVOID PHAPI PhAlloc(SIZE_T Size);
+NPHAPI PVOID PHAPI PhRealloc(PVOID Memory, SIZE_T Size);
+NPHAPI VOID PHAPI PhFree(PVOID Memory);
+PVOID PHAPI PhGetProcAddress(PWSTR LibraryName, PSTR ProcName);
+NPHAPI VOID PHAPI PhVoid();
+
+#endif
diff --git a/branches/ph-plugins/NProcessHacker/obj.c b/branches/ph-plugins/NProcessHacker/obj.c
new file mode 100644
index 000000000..2dfa285d6
--- /dev/null
+++ b/branches/ph-plugins/NProcessHacker/obj.c
@@ -0,0 +1,157 @@
+/*
+ * Process Hacker Library
+ *
+ * Copyright (C) 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 .
+ */
+
+#include "obj.h"
+
+ULONG PHAPI PhpQueryFileObjectThreadStart(
+ PVOID Parameter
+ );
+
+_NtQueryObject NtQueryObject = NULL;
+
+HANDLE QueryFileObjectThreadHandle = NULL;
+PVOID QueryFileObjectFiber = NULL;
+CRITICAL_SECTION QueryFileObjectCs;
+HANDLE QueryFileObjectStartEvent = NULL;
+HANDLE QueryFileObjectCompletedEvent = NULL;
+HANDLE QueryFileObjectFileHandle;
+PH_QUERY_FILE_OBJECT_BUFFER QueryFileObjectBuffer;
+
+NTSTATUS PHAPI PhObjInit()
+{
+ if (!(NtQueryObject = (_NtQueryObject)
+ PhGetProcAddress(L"ntdll.dll", "NtQueryObject")))
+ return STATUS_PROCEDURE_NOT_FOUND;
+
+ InitializeCriticalSection(&QueryFileObjectCs);
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS PHAPI PhQueryNameFileObject(
+ HANDLE FileHandle,
+ POBJECT_NAME_INFORMATION FileObjectNameInformation,
+ ULONG FileObjectNameInformationLength,
+ PULONG ReturnLength
+ )
+{
+ ULONG waitResult;
+
+ EnterCriticalSection(&QueryFileObjectCs);
+
+ /* Create a query thread if we don't have one. */
+ if (!QueryFileObjectThreadHandle)
+ {
+ QueryFileObjectThreadHandle = CreateThread(
+ NULL, 0, (LPTHREAD_START_ROUTINE)PhpQueryFileObjectThreadStart, NULL, 0, NULL);
+
+ if (!QueryFileObjectThreadHandle)
+ {
+ LeaveCriticalSection(&QueryFileObjectCs);
+ return STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ /* Create the events if they don't exist. */
+ if (!QueryFileObjectStartEvent)
+ if (!(QueryFileObjectStartEvent = CreateEvent(NULL, FALSE, FALSE, NULL)))
+ return STATUS_UNSUCCESSFUL;
+ if (!QueryFileObjectCompletedEvent)
+ if (!(QueryFileObjectCompletedEvent = CreateEvent(NULL, FALSE, FALSE, NULL)))
+ return STATUS_UNSUCCESSFUL;
+
+ /* Initialize the work context. */
+ QueryFileObjectFileHandle = FileHandle;
+ QueryFileObjectBuffer.Length = FileObjectNameInformationLength;
+ QueryFileObjectBuffer.Name = FileObjectNameInformation;
+ QueryFileObjectBuffer.Initialized = TRUE;
+ /* Allow the worker thread to start. */
+ SetEvent(QueryFileObjectStartEvent);
+ /* Wait for the work to complete, with a timeout of 1 second. */
+ waitResult = WaitForSingleObject(QueryFileObjectCompletedEvent, 1000);
+ /* Set the buffer as uninitialized. */
+ QueryFileObjectBuffer.Initialized = FALSE;
+
+ /* Return normally if the work was completed. */
+ if (waitResult == WAIT_OBJECT_0)
+ {
+ NTSTATUS status;
+ ULONG returnLength;
+
+ /* Copy the status information before we leave the critical section. */
+ status = QueryFileObjectBuffer.Status;
+ returnLength = QueryFileObjectBuffer.ReturnLength;
+ LeaveCriticalSection(&QueryFileObjectCs);
+
+ if (ReturnLength)
+ *ReturnLength = returnLength;
+
+ return status;
+ }
+ /* Kill the worker thread if it took too long. */
+ /* else if (waitResult == WAIT_TIMEOUT) */
+ else
+ {
+ /* Kill the thread. */
+ if (TerminateThread(QueryFileObjectThreadHandle, 1))
+ {
+ QueryFileObjectThreadHandle = NULL;
+
+ /* Delete the fiber (and free the thread stack). */
+ DeleteFiber(QueryFileObjectFiber);
+ QueryFileObjectFiber = NULL;
+ }
+
+ LeaveCriticalSection(&QueryFileObjectCs);
+ return STATUS_UNSUCCESSFUL;
+ }
+}
+
+ULONG PHAPI PhpQueryFileObjectThreadStart(
+ PVOID Parameter
+ )
+{
+ QueryFileObjectFiber = ConvertThreadToFiber(Parameter);
+
+ while (TRUE)
+ {
+ /* Wait for work. */
+ if (WaitForSingleObject(QueryFileObjectStartEvent, INFINITE) != WAIT_OBJECT_0)
+ continue;
+
+ /* Make sure we actually have work. */
+ if (QueryFileObjectBuffer.Initialized)
+ {
+ QueryFileObjectBuffer.Status = NtQueryObject(
+ QueryFileObjectFileHandle,
+ ObjectNameInformation,
+ QueryFileObjectBuffer.Name,
+ QueryFileObjectBuffer.Length,
+ &QueryFileObjectBuffer.ReturnLength
+ );
+
+ /* Work done. */
+ SetEvent(QueryFileObjectCompletedEvent);
+ }
+ }
+
+ return 0;
+}
diff --git a/branches/ph-plugins/NProcessHacker/obj.h b/branches/ph-plugins/NProcessHacker/obj.h
new file mode 100644
index 000000000..f98f35520
--- /dev/null
+++ b/branches/ph-plugins/NProcessHacker/obj.h
@@ -0,0 +1,46 @@
+/*
+ * Process Hacker Library
+ *
+ * Copyright (C) 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 .
+ */
+
+#ifndef _OBJ_H
+#define _OBJ_H
+
+#include "nph.h"
+#include "nativedefs.h"
+
+typedef struct _PH_QUERY_FILE_OBJECT_BUFFER
+{
+ LOGICAL Initialized;
+ NTSTATUS Status;
+ ULONG Length;
+ ULONG ReturnLength;
+ POBJECT_NAME_INFORMATION Name;
+} PH_QUERY_FILE_OBJECT_BUFFER, *PPH_QUERY_FILE_OBJECT_BUFFER;
+
+NTSTATUS PHAPI PhObjInit();
+
+NPHAPI NTSTATUS PHAPI PhQueryNameFileObject(
+ HANDLE FileHandle,
+ POBJECT_NAME_INFORMATION FileObjectNameInformation,
+ ULONG FileObjectNameInformationLength,
+ PULONG ReturnLength
+ );
+
+#endif
diff --git a/branches/ph-plugins/NProcessHacker/process.c b/branches/ph-plugins/NProcessHacker/process.c
new file mode 100644
index 000000000..52c0b35d9
--- /dev/null
+++ b/branches/ph-plugins/NProcessHacker/process.c
@@ -0,0 +1,117 @@
+/*
+ * Process Hacker Library
+ *
+ * Copyright (C) 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 .
+ */
+
+#include "process.h"
+
+NTSTATUS PHAPI PhQueryProcessWs(
+ HANDLE ProcessHandle,
+ WS_INFORMATION_CLASS WsInformationClass,
+ PVOID WsInformation,
+ ULONG WsInformationLength,
+ PULONG ReturnLength
+ )
+{
+ switch (WsInformationClass)
+ {
+ case WsCount:
+ case WsPrivateCount:
+ case WsSharedCount:
+ case WsShareableCount:
+ if (WsInformationLength < 4)
+ return STATUS_BUFFER_TOO_SMALL;
+ goto WsCounters;
+ case WsAllCounts:
+ if (WsInformationLength < sizeof(WS_ALL_COUNTS))
+ return STATUS_BUFFER_TOO_SMALL;
+WsCounters:
+ {
+ PROCESS_MEMORY_COUNTERS procMem;
+ ULONG count = 0;
+ ULONG privateCount = 0;
+ ULONG sharedCount = 0;
+ ULONG shareableCount = 0;
+ PPSAPI_WORKING_SET_INFORMATION wsInfo;
+ SIZE_T wsInfoLength;
+ ULONG i;
+
+ if (!GetProcessMemoryInfo(ProcessHandle, &procMem, sizeof(procMem)))
+ return STATUS_UNSUCCESSFUL;
+
+ /* Assume the page size is 4kB */
+ wsInfoLength = sizeof(PSAPI_WORKING_SET_INFORMATION) +
+ sizeof(PSAPI_WORKING_SET_BLOCK) * (procMem.WorkingSetSize / 4096);
+ wsInfo = (PPSAPI_WORKING_SET_INFORMATION)PhAlloc(wsInfoLength);
+
+ if (!QueryWorkingSet(ProcessHandle, wsInfo, wsInfoLength))
+ {
+ PhFree(wsInfo);
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ for (i = 0; i < wsInfo->NumberOfEntries; i++)
+ {
+ PSAPI_WORKING_SET_BLOCK block = wsInfo->WorkingSetInfo[i];
+
+ count++;
+
+ if (block.ShareCount > 1)
+ sharedCount++;
+ if (block.ShareCount == 0)
+ privateCount++;
+ if (block.Shared)
+ shareableCount++;
+ }
+
+ PhFree(wsInfo);
+
+ switch (WsInformationClass)
+ {
+ case WsCount:
+ *(PULONG)WsInformation = count;
+ break;
+ case WsPrivateCount:
+ *(PULONG)WsInformation = privateCount;
+ break;
+ case WsSharedCount:
+ *(PULONG)WsInformation = sharedCount;
+ break;
+ case WsShareableCount:
+ *(PULONG)WsInformation = shareableCount;
+ break;
+ case WsAllCounts:
+ {
+ PWS_ALL_COUNTS allCounts = (PWS_ALL_COUNTS)WsInformation;
+
+ allCounts->Count = count;
+ allCounts->PrivateCount = privateCount;
+ allCounts->SharedCount = sharedCount;
+ allCounts->ShareableCount = shareableCount;
+ break;
+ }
+ }
+
+ return STATUS_SUCCESS;
+ }
+ break;
+ default:
+ return STATUS_INVALID_PARAMETER;
+ }
+}
diff --git a/branches/ph-plugins/NProcessHacker/process.h b/branches/ph-plugins/NProcessHacker/process.h
new file mode 100644
index 000000000..6d795ebc0
--- /dev/null
+++ b/branches/ph-plugins/NProcessHacker/process.h
@@ -0,0 +1,53 @@
+/*
+ * Process Hacker Library
+ *
+ * Copyright (C) 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 .
+ */
+
+#ifndef _PROCESS_H
+#define _PROCESS_H
+
+#include "nph.h"
+#include
+
+typedef enum _WS_INFORMATION_CLASS
+{
+ WsCount = 0,
+ WsPrivateCount,
+ WsSharedCount,
+ WsShareableCount,
+ WsAllCounts
+} WS_INFORMATION_CLASS, *PWS_INFORMATION_CLASS;
+
+typedef struct _WS_ALL_COUNTS
+{
+ ULONG Count;
+ ULONG PrivateCount;
+ ULONG SharedCount;
+ ULONG ShareableCount;
+} WS_ALL_COUNTS, *PWS_ALL_COUNTS;
+
+NPHAPI NTSTATUS PHAPI PhQueryProcessWs(
+ HANDLE ProcessHandle,
+ WS_INFORMATION_CLASS WsInformationClass,
+ PVOID WsInformation,
+ ULONG WsInformationLength,
+ PULONG ReturnLength
+ );
+
+#endif
\ No newline at end of file
diff --git a/branches/ph-plugins/NProcessHacker/resource.rc b/branches/ph-plugins/NProcessHacker/resource.rc
new file mode 100644
index 000000000..337d53ea5
--- /dev/null
+++ b/branches/ph-plugins/NProcessHacker/resource.rc
@@ -0,0 +1,53 @@
+#include
+
+#define VER_COMMA 1,5,0,0
+#define VER_STR "1.5\0"
+
+#define VER_FILEVERSION VER_COMMA
+#define VER_FILEVERSION_STR VER_STR
+#define VER_PRODUCTVERSION VER_COMMA
+#define VER_PRODUCTVERSION_STR VER_STR
+
+#ifndef DEBUG
+#define VER_DEBUG 0
+#else
+#define VER_DEBUG VS_FF_DEBUG
+#endif
+
+#define VER_PRIVATEBUILD 0
+#define VER_PRERELEASE 0
+
+#define VER_COMPANYNAME_STR "wj32\0"
+#define VER_FILEDESCRIPTION_STR "Process Hacker Library\0"
+#define VER_LEGALCOPYRIGHT_STR "Copyright (c) 2009 wj32. Licensed under the GNU GPL, v3.\0"
+#define VER_ORIGINALFILENAME_STR "NProcessHacker.dll\0"
+#define VER_PRODUCTNAME_STR "Process Hacker\0"
+
+VS_VERSION_INFO VERSIONINFO
+FILEVERSION VER_FILEVERSION
+PRODUCTVERSION VER_PRODUCTVERSION
+FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+FILEFLAGS (VER_PRIVATEBUILD | VER_PRERELEASE | VER_DEBUG)
+FILEOS VOS__WINDOWS32
+FILETYPE VFT_DLL
+FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904E4"
+ BEGIN
+ VALUE "CompanyName", VER_COMPANYNAME_STR
+ VALUE "FileDescription", VER_FILEDESCRIPTION_STR
+ VALUE "FileVersion", VER_FILEVERSION_STR
+ VALUE "LegalCopyright", VER_LEGALCOPYRIGHT_STR
+ VALUE "OriginalFilename", VER_ORIGINALFILENAME_STR
+ VALUE "ProductName", VER_PRODUCTNAME_STR
+ VALUE "ProductVersion", VER_PRODUCTVERSION_STR
+ END
+ END
+
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1252
+ END
+END
diff --git a/branches/ph-plugins/NProcessHacker/secedit.c b/branches/ph-plugins/NProcessHacker/secedit.c
new file mode 100644
index 000000000..944056ccf
--- /dev/null
+++ b/branches/ph-plugins/NProcessHacker/secedit.c
@@ -0,0 +1,24 @@
+/*
+ * Process Hacker Library
+ *
+ * Copyright (C) 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 .
+ */
+
+#include "secedit.h"
+
+
diff --git a/branches/ph-plugins/NProcessHacker/secedit.h b/branches/ph-plugins/NProcessHacker/secedit.h
new file mode 100644
index 000000000..df33f1cde
--- /dev/null
+++ b/branches/ph-plugins/NProcessHacker/secedit.h
@@ -0,0 +1,48 @@
+/*
+ * Process Hacker Library
+ *
+ * Copyright (C) 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 .
+ */
+
+#ifndef _SECEDIT_H
+#define _SECEDIT_H
+
+#include "nph.h"
+
+typedef HRESULT (__stdcall *_QueryInterface)(
+ PVOID This,
+ REFIID Id,
+ PVOID *Object
+ );
+
+typedef ULONG (__stdcall *_AddRef)(
+ PVOID This
+ );
+
+typedef ULONG (__stdcall *_Release)(
+ PVOID This
+ );
+
+typedef struct _ISECURITY_INFORMATION
+{
+ _QueryInterface QueryInterface;
+ _AddRef AddRef;
+ _Release Release;
+} ISECURITY_INFORMATION, *PISECURITY_INFORMATION;
+
+#endif
diff --git a/branches/ph-plugins/NProcessHacker/verify.c b/branches/ph-plugins/NProcessHacker/verify.c
new file mode 100644
index 000000000..6718a09d1
--- /dev/null
+++ b/branches/ph-plugins/NProcessHacker/verify.c
@@ -0,0 +1,200 @@
+/*
+ * Process Hacker Library
+ *
+ * Copyright (C) 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 .
+ */
+
+#include "verify.h"
+
+VERIFY_RESULT PHAPI PhpStatusToVerifyResult(LONG Status);
+VERIFY_RESULT PHAPI PhpVerifyFileBasic(PWSTR FileName);
+VERIFY_RESULT PHAPI PhpVerifyFileCat(PWSTR FileName);
+
+_CryptCATAdminCalcHashFromFileHandle CryptCATAdminCalcHashFromFileHandle;
+_CryptCATAdminAcquireContext CryptCATAdminAcquireContext;
+_CryptCATAdminEnumCatalogFromHash CryptCATAdminEnumCatalogFromHash;
+_CryptCATCatalogInfoFromContext CryptCATCatalogInfoFromContext;
+_CryptCATAdminReleaseCatalogContext CryptCATAdminReleaseCatalogContext;
+_CryptCATAdminReleaseContext CryptCATAdminReleaseContext;
+
+NTSTATUS PHAPI PhVerifyInit()
+{
+ LoadLibrary(L"wintrust.dll");
+
+ CryptCATAdminCalcHashFromFileHandle =
+ PhGetProcAddress(L"wintrust.dll", "CryptCATAdminCalcHashFromFileHandle");
+ CryptCATAdminAcquireContext =
+ PhGetProcAddress(L"wintrust.dll", "CryptCATAdminAcquireContext");
+ CryptCATAdminEnumCatalogFromHash =
+ PhGetProcAddress(L"wintrust.dll", "CryptCATAdminEnumCatalogFromHash");
+ CryptCATCatalogInfoFromContext =
+ PhGetProcAddress(L"wintrust.dll", "CryptCATCatalogInfoFromContext");
+ CryptCATAdminReleaseCatalogContext =
+ PhGetProcAddress(L"wintrust.dll", "CryptCATAdminReleaseCatalogContext");
+ CryptCATAdminReleaseContext =
+ PhGetProcAddress(L"wintrust.dll", "CryptCATAdminReleaseContext");
+
+ return STATUS_SUCCESS;
+}
+
+VERIFY_RESULT PHAPI PhpStatusToVerifyResult(LONG Status)
+{
+ switch (Status)
+ {
+ case 0:
+ return VrTrusted;
+ case TRUST_E_NOSIGNATURE:
+ return VrNoSignature;
+ case CERT_E_EXPIRED:
+ return VrExpired;
+ case CERT_E_REVOKED:
+ return VrRevoked;
+ case TRUST_E_EXPLICIT_DISTRUST:
+ return VrDistrust;
+ case CRYPT_E_SECURITY_SETTINGS:
+ return VrSecuritySettings;
+ default:
+ return VrSecuritySettings;
+ }
+}
+
+VERIFY_RESULT PHAPI PhpVerifyFileBasic(PWSTR FileName)
+{
+ WINTRUST_DATA trustData = { 0 };
+ WINTRUST_FILE_INFO fileInfo = { 0 };
+ GUID actionGenericVerifyV2 = WINTRUST_ACTION_GENERIC_VERIFY_V2;
+
+ fileInfo.cbStruct = sizeof(fileInfo);
+ fileInfo.pcwszFilePath = FileName;
+
+ trustData.cbStruct = sizeof(trustData);
+ trustData.dwUIChoice = WTD_UI_NONE;
+ trustData.dwProvFlags = WTD_SAFER_FLAG;
+ trustData.dwUnionChoice = WTD_CHOICE_FILE;
+ trustData.pFile = &fileInfo;
+
+ return PhpStatusToVerifyResult(WinVerifyTrust(NULL, &actionGenericVerifyV2, &trustData));
+}
+
+VERIFY_RESULT PHAPI PhpVerifyFileCat(PWSTR FileName)
+{
+ LONG status = TRUST_E_NOSIGNATURE;
+ WINTRUST_DATA trustData = { 0 };
+ WINTRUST_CATALOG_INFO catalogInfo = { 0 };
+ GUID driverActionVerify = DRIVER_ACTION_VERIFY;
+ HANDLE fileHandle;
+ PBYTE fileHash = NULL;
+ ULONG fileHashLength;
+ PWSTR fileHashTag = NULL;
+ HANDLE catAdminHandle = NULL;
+ HANDLE catInfoHandle = NULL;
+ ULONG i;
+
+ fileHandle = CreateFile(
+ FileName,
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL
+ );
+
+ if (fileHandle == INVALID_HANDLE_VALUE)
+ return VrNoSignature;
+
+ fileHashLength = 256;
+ fileHash = (PBYTE)PhAlloc(fileHashLength);
+
+ if (!CryptCATAdminCalcHashFromFileHandle(fileHandle, &fileHashLength, fileHash, 0))
+ {
+ fileHash = (PBYTE)PhRealloc(fileHash, fileHashLength);
+
+ if (!CryptCATAdminCalcHashFromFileHandle(fileHandle, &fileHashLength, fileHash, 0))
+ {
+ CloseHandle(fileHandle);
+ PhFree(fileHash);
+ return VrNoSignature;
+ }
+ }
+
+ if (!CryptCATAdminAcquireContext(&catAdminHandle, &driverActionVerify, 0))
+ {
+ CloseHandle(fileHandle);
+ PhFree(fileHash);
+ return VrNoSignature;
+ }
+
+ fileHashTag = (PWSTR)PhAlloc((fileHashLength * 2 + 1) * sizeof(WCHAR));
+
+ for (i = 0; i < fileHashLength; i++)
+ wsprintfW(&fileHashTag[i * 2], L"%02X", fileHash[i]);
+
+ catInfoHandle = CryptCATAdminEnumCatalogFromHash(
+ catAdminHandle,
+ fileHash,
+ fileHashLength,
+ 0,
+ NULL
+ );
+
+ PhFree(fileHash);
+
+ if (catInfoHandle)
+ {
+ CATALOG_INFO ci = { 0 };
+
+ if (CryptCATCatalogInfoFromContext(catInfoHandle, &ci, 0))
+ {
+ catalogInfo.cbStruct = sizeof(catalogInfo);
+ catalogInfo.pcwszCatalogFilePath = ci.wszCatalogFile;
+ catalogInfo.pcwszMemberFilePath = FileName;
+ catalogInfo.pcwszMemberTag = fileHashTag;
+
+ trustData.cbStruct = sizeof(trustData);
+ trustData.dwUIChoice = WTD_UI_NONE;
+ trustData.fdwRevocationChecks = WTD_STATEACTION_VERIFY;
+ trustData.dwUnionChoice = WTD_CHOICE_CATALOG;
+ trustData.pCatalog = &catalogInfo;
+
+ status = WinVerifyTrust(NULL, &driverActionVerify, &trustData);
+ }
+
+ CryptCATAdminReleaseCatalogContext(catAdminHandle, catInfoHandle, 0);
+ }
+
+ PhFree(fileHashTag);
+ CryptCATAdminReleaseContext(catAdminHandle, 0);
+ CloseHandle(fileHandle);
+
+ return PhpStatusToVerifyResult(status);
+}
+
+VERIFY_RESULT PHAPI PhVerifyFile(PWSTR FileName)
+{
+ VERIFY_RESULT result = VrNoSignature;
+
+ result = PhpVerifyFileBasic(FileName);
+
+ if (result == VrNoSignature)
+ {
+ result = PhpVerifyFileCat(FileName);
+ }
+
+ return result;
+}
diff --git a/branches/ph-plugins/NProcessHacker/verify.h b/branches/ph-plugins/NProcessHacker/verify.h
new file mode 100644
index 000000000..4c950a1fb
--- /dev/null
+++ b/branches/ph-plugins/NProcessHacker/verify.h
@@ -0,0 +1,88 @@
+/*
+ * Process Hacker Library
+ *
+ * Copyright (C) 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 .
+ */
+
+#ifndef _VERIFY_H
+#define _VERIFY_H
+
+#include "nph.h"
+#include
+#include
+
+typedef enum _VERIFY_RESULT
+{
+ VrUnknown = 0,
+ VrNoSignature,
+ VrTrusted,
+ VrTrustedInstaller,
+ VrExpired,
+ VrRevoked,
+ VrDistrust,
+ VrSecuritySettings
+} VERIFY_RESULT, *PVERIFY_RESULT;
+
+typedef struct _CATALOG_INFO
+{
+ DWORD cbStruct;
+ WCHAR wszCatalogFile[MAX_PATH];
+} CATALOG_INFO, *PCATALOG_INFO;
+
+typedef BOOL (WINAPI *_CryptCATAdminCalcHashFromFileHandle)(
+ HANDLE hFile,
+ DWORD *pcbHash,
+ BYTE *pbHash,
+ DWORD dwFlags
+ );
+
+typedef BOOL (WINAPI *_CryptCATAdminAcquireContext)(
+ HANDLE *phCatAdmin,
+ GUID *pgSubsystem,
+ DWORD dwFlags
+ );
+
+typedef HANDLE (WINAPI *_CryptCATAdminEnumCatalogFromHash)(
+ HANDLE hCatAdmin,
+ BYTE *pbHash,
+ DWORD cbHash,
+ DWORD dwFlags,
+ HANDLE *phPrevCatInfo
+ );
+
+typedef BOOL (WINAPI *_CryptCATCatalogInfoFromContext)(
+ HANDLE hCatInfo,
+ CATALOG_INFO *psCatInfo,
+ DWORD dwFlags
+ );
+
+typedef BOOL (WINAPI *_CryptCATAdminReleaseCatalogContext)(
+ HANDLE hCatAdmin,
+ HANDLE hCatInfo,
+ DWORD dwFlags
+ );
+
+typedef BOOL (WINAPI *_CryptCATAdminReleaseContext)(
+ HANDLE hCatAdmin,
+ DWORD dwFlags
+ );
+
+NTSTATUS PHAPI PhVerifyInit();
+NPHAPI VERIFY_RESULT PHAPI PhVerifyFile(PWSTR FileName);
+
+#endif
\ No newline at end of file
diff --git a/branches/ph-plugins/NProcessHacker/x64/Release/NProcessHacker.dll b/branches/ph-plugins/NProcessHacker/x64/Release/NProcessHacker.dll
new file mode 100644
index 000000000..3e91b4c99
Binary files /dev/null and b/branches/ph-plugins/NProcessHacker/x64/Release/NProcessHacker.dll differ
diff --git a/branches/ph-plugins/ProcessHacker.Common/BaseConverter.cs b/branches/ph-plugins/ProcessHacker.Common/BaseConverter.cs
new file mode 100644
index 000000000..52bd7687d
--- /dev/null
+++ b/branches/ph-plugins/ProcessHacker.Common/BaseConverter.cs
@@ -0,0 +1,176 @@
+/*
+ * Process Hacker -
+ * base converter
+ *
+ * Copyright (C) 2006-2008 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.Text;
+
+namespace ProcessHacker.Common
+{
+ ///
+ /// Contains methods to parse numbers from string representations using different bases.
+ ///
+ public static class BaseConverter
+ {
+ private static int[] _reverseChars = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 37, 38, 39, 40, 41,
+ 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 52,
+ 53, 54, 55, 56, 57, 58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, 60, 61, 62, 63, 64, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
+ 33, 34, 35, 65, 66, 67, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ ///
+ /// Reverses a string.
+ ///
+ /// The string to be reversed
+ /// The reversed string.
+ public static string ReverseString(string str)
+ {
+ StringBuilder sb = new StringBuilder();
+
+ for (int i = str.Length - 1; i >= 0; i--)
+ {
+ sb.Append(str[i]);
+ }
+
+ return sb.ToString();
+ }
+
+ ///
+ /// Converts a string to a number using the specified base.
+ ///
+ ///
+ /// This function does not parse prefixes; to do so, use
+ ///
+ /// The string to convert
+ /// The base to use
+ ///
+ public static decimal ToNumber(string number, int b)
+ {
+ if (b > 70)
+ return 0;
+
+ if (number == "")
+ return 0;
+
+ bool negative = number[0] == '-';
+ int length = number.Length;
+ long result = 0;
+
+ if (negative)
+ {
+ length -= 1;
+ }
+
+ number = ReverseString(number).ToLower();
+
+ for (int i = 0; i < length; i++)
+ {
+ result += _reverseChars[number[i]] * ((long)Math.Pow(b, i));
+ }
+
+ if (negative)
+ return -result;
+ else
+ return result;
+ }
+
+ ///
+ /// Converts a string to a number, parsing prefixes to determine the base.
+ ///
+ /// The string to convert.
+ ///
+ public static decimal ToNumberParse(string number)
+ {
+ return ToNumberParse(number, true);
+ }
+
+ ///
+ /// Converts a string to a number, parsing prefixes to determine the base.
+ ///
+ /// The string to convert.
+ /// Enables or disables non-standard prefixes for
+ /// bases 2 (b), 3 (t), 4 (q), 12 (w) and 32 (r).
+ ///
+ public static decimal ToNumberParse(string number, bool allowNonStandardExts)
+ {
+ if (number == "")
+ return 0;
+
+ bool negative = number[0] == '-';
+ decimal result = 0;
+
+ if (negative)
+ number = number.Substring(1);
+
+ if (number.Length > 2 && (number.Substring(0, 2) == "0x")) // hexadecimal
+ {
+ result = ToNumber(number.Substring(2), 16);
+ }
+ else if (number.Length > 1)
+ {
+ if (number[0] == '0') // octal
+ {
+ result = ToNumber(number.Substring(1), 8);
+ }
+ else if (number[0] == 'b' && allowNonStandardExts) // binary
+ {
+ result = ToNumber(number.Substring(1), 2);
+ }
+ else if (number[0] == 't' && allowNonStandardExts) // ternary
+ {
+ result = ToNumber(number.Substring(1), 3);
+ }
+ else if (number[0] == 'q' && allowNonStandardExts) // quaternary
+ {
+ result = ToNumber(number.Substring(1), 4);
+ }
+ else if (number[0] == 'w' && allowNonStandardExts) // base 12
+ {
+ result = ToNumber(number.Substring(1), 12);
+ }
+ else if (number[0] == 'r' && allowNonStandardExts) // base 32
+ {
+ result = ToNumber(number.Substring(1), 32);
+ }
+ else // base 10
+ {
+ result = ToNumber(number, 10);
+ }
+ }
+ else // base 10
+ {
+ result = ToNumber(number, 10);
+ }
+
+ if (negative)
+ return -result;
+ else
+ return result;
+ }
+ }
+}
diff --git a/branches/ph-plugins/ProcessHacker.Common/ByteStreamReader.cs b/branches/ph-plugins/ProcessHacker.Common/ByteStreamReader.cs
new file mode 100644
index 000000000..37026970a
--- /dev/null
+++ b/branches/ph-plugins/ProcessHacker.Common/ByteStreamReader.cs
@@ -0,0 +1,118 @@
+/*
+ * Process Hacker -
+ * byte stream reader
+ *
+ * Copyright (C) 2008-2009 wj32
+ *
+ * This file is part of PNG.Net.
+ *
+ * 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.IO;
+using System.Text;
+
+namespace ProcessHacker.Common
+{
+ public sealed class ByteStreamReader : Stream
+ {
+ private byte[] _data;
+ private long _position;
+
+ public ByteStreamReader(byte[] data)
+ {
+ _data = data;
+ _position = 0;
+ }
+
+ public override bool CanRead
+ {
+ get { return true; }
+ }
+
+ public override bool CanSeek
+ {
+ get { return true; }
+ }
+
+ public override bool CanWrite
+ {
+ get { return false; }
+ }
+
+ public override void Flush()
+ {
+ // don't need to flush
+ }
+
+ public override long Length
+ {
+ get { return _data.LongLength; }
+ }
+
+ public override long Position
+ {
+ get { return _position; }
+ set { _position = value; }
+ }
+
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ long length = (_position + count > _data.Length) ? _data.Length - _position - 1 : count;
+
+ if (_position >= _data.Length)
+ return 0;
+
+ for (long i = 0; i < length; i++, _position++)
+ buffer[offset + i] = _data[_position];
+
+ return (int)length;
+ }
+
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ switch (origin)
+ {
+ case SeekOrigin.Begin:
+ _position = offset;
+ break;
+
+ case SeekOrigin.Current:
+ _position += offset;
+ break;
+
+ case SeekOrigin.End:
+ _position = _data.Length - 1 + offset;
+ break;
+
+ default:
+ throw new ArgumentException();
+ }
+
+ return _position;
+ }
+
+ public override void SetLength(long value)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ throw new NotSupportedException();
+ }
+ }
+}
diff --git a/branches/ph-plugins/ProcessHacker.Common/CircularBuffer.cs b/branches/ph-plugins/ProcessHacker.Common/CircularBuffer.cs
new file mode 100644
index 000000000..546023a12
--- /dev/null
+++ b/branches/ph-plugins/ProcessHacker.Common/CircularBuffer.cs
@@ -0,0 +1,369 @@
+/*
+ * Process Hacker -
+ * circular buffer
+ *
+ * Copyright (C) 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;
+
+namespace ProcessHacker.Common
+{
+ ///
+ /// Provides methods for manipulating a circular buffer. A circular buffer
+ /// is a fixed-size array where old elements will be automatically deleted
+ /// as new elements are added.
+ ///
+ ///
+ /// This data structure is not thread-safe. You must provide your own
+ /// synchronization if more than one thread reads from or writes to the
+ /// buffer.
+ ///
+ ///
+ /// Ten-element circular buffer:
+ /// Data array: [4] [3] [2] [1] [0] [9] [8] [7] [6] [5]
+ /// ^ most recent data
+ /// ^ index
+ ///
+ public class CircularBuffer : IList
+ {
+ private int _size;
+ private int _count;
+ private int _index;
+ private T[] _data;
+
+ ///
+ /// Creates a new circular buffer of the specified size.
+ ///
+ /// The size of the buffer.
+ public CircularBuffer(int size)
+ {
+ /*
+ * [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ]
+ * ^ _index
+ */
+ _size = size;
+ _count = 0;
+ _index = 0;
+ _data = new T[size];
+ }
+
+ ///
+ /// Gets or sets an element in the buffer. This is guaranteed to
+ /// never throw an exception.
+ ///
+ ///
+ /// A zero-based index into the buffer. Index 0 contains the
+ /// most recently added item, and higher positive indicies
+ /// access less recent items. Index -1 contains the least recently
+ /// added item, and lower negative indicies access more recent
+ /// items.
+ ///
+ public T this[int index]
+ {
+ get
+ {
+ /*
+ * For example, if _index = 6 and index = 5:
+ *
+ * [5] [4] [3] [2] [1] [0] [9] [8] [7] [6]
+ * ^ _index
+ * ^ (_index + index) mod _size = 11 mod 10 = 1
+ */
+
+ // See the comment in Add for more details on modulus.
+ return _data[(((_index + index) % _size) + _size) % _size];
+ }
+ set
+ {
+ // See the comment in Add for more details.
+ _data[(((_index + index) % _size) + _size) % _size] = value;
+ }
+ }
+
+ ///
+ /// Gets the number of elements stored in the buffer.
+ ///
+ public int Count
+ {
+ get { return _count; }
+ }
+
+ ///
+ /// Gets the maximum number of elements that can be stored in
+ /// the buffer.
+ ///
+ public int Size
+ {
+ get { return _size; }
+ }
+
+ ///
+ /// Adds an element to the buffer. If the maximum buffer size
+ /// has been reached, the least recently added element will
+ /// be erased by the new element.
+ ///
+ /// The element to add.
+ public void Add(T value)
+ {
+ /*
+ * To add an item to the circular buffer the index is
+ * decremented and a modulus is performed on it to ensure
+ * it is not negative.
+ *
+ * [5] [4] [3] [2] [1] [0] [9] [8] [7] [6]
+ * ^ _index (6)
+ * When the new element x is added:
+ * [5] [4] [3] [2] [1] [x] [9] [8] [7] [6]
+ * ^ _index (5)
+ *
+ * Another example:
+ * [9] [8] [7] [6] [5] [4] [3] [2] [1] [0]
+ * ^ _index (0)
+ * When the new element x is added:
+ * [9] [8] [7] [6] [5] [4] [3] [2] [1] [x]
+ * ^ _index (9)
+ * = -1 mod 10 = 9
+ */
+
+ /* The C# modulus operator produces a result which has the
+ * same sign as the dividend. For circular array access,
+ * we want the result to have the same sign as the divisor.
+ * We do this by using r = ((i % t) + t) % t where i is
+ * the index (possibly negative) and t is the size of the
+ * array.
+ */
+ _data[_index = (((_index - 1) % _size) + _size) % _size] = value;
+
+ if (_count < _size)
+ _count++;
+ }
+
+ ///
+ /// Resizes the circular buffer.
+ ///
+ /// The new maximum buffer size.
+ public void Resize(int newSize)
+ {
+ // If we're not actually resizing the thing...
+ if (newSize == _size)
+ return;
+
+ T[] newArray = new T[newSize];
+ int tailSize = (_size - _index) % _size;
+ int headSize = _count - tailSize;
+
+ /*
+ * The tail contains the most recent data.
+ * [3] [2] [1] [0] [ ] [8] [7] [6] [5] [4]
+ * [ ... head ... ] [ ..... tail ..... ]
+ * ^ _index (5)
+ * tailSize = _size - _index = 5
+ * headSize = _count - tailSize = 9 - 5 = 4
+ */
+
+ // If the new buffer is bigger than the current one.
+ if (newSize > _size)
+ {
+ /*
+ * Copy the tail, then the head.
+ * This means that the tail will now be at the front.
+ * [8] [7] [6] [5] [4] [3] [2] [1] [0] [ ] [ ] [ ]
+ * [ ..... tail ..... ][ ... head ... ]
+ * ^ _index (0)
+ */
+ Array.Copy(_data, _index, newArray, 0, tailSize);
+ Array.Copy(_data, 0, newArray, tailSize, headSize);
+ _index = 0;
+ }
+ // If the new buffer is smaller than the current one.
+ else if (newSize < _size)
+ {
+ // If the new buffer is smaller than (or equal to) the tail size.
+ if (tailSize >= newSize)
+ {
+ /*
+ * Copy only a part of the tail because we don't have enough room.
+ * [8] [7] [6]
+ * [ . tail . ]
+ * ^ _index (0)
+ */
+ Array.Copy(_data, _index, newArray, 0, newSize);
+ _index = 0;
+ }
+ // If the new buffer is bigger than the tail size.
+ else
+ {
+ /*
+ * Copy the tail in full, then copy a part of the head.
+ * [8] [7] [6] [5] [4] [3] [2]
+ * [ ..... tail ..... ][ head ]
+ * ^ _index (0)
+ */
+ Array.Copy(_data, _index, newArray, 0, tailSize);
+ Array.Copy(_data, 0, newArray, tailSize, newSize - tailSize);
+ _index = 0;
+ }
+
+ // The number of elements obviously can't be bigger than the
+ // buffer size.
+ if (_count > newSize)
+ _count = newSize;
+ }
+
+ _data = newArray;
+ _size = newSize;
+ }
+
+ ///
+ /// Converts the buffer to an array.
+ ///
+ ///
+ public T[] ToArray()
+ {
+ T[] newArray = new T[this.Count];
+
+ this.CopyTo(newArray, 0);
+
+ return newArray;
+ }
+
+ #region IList Members
+
+ ///
+ /// Gets the index of the specified element in the array.
+ ///
+ /// The element to search for.
+ /// A positive index if the element was found. Otherwise, -1.
+ public int IndexOf(T item)
+ {
+ for (int i = 0; i < this.Count; i++)
+ if (this[i].Equals(item))
+ return i;
+
+ return -1;
+ }
+
+ ///
+ /// This method is not supported.
+ ///
+ public void Insert(int index, T item)
+ {
+ throw new NotSupportedException();
+ }
+
+ ///
+ /// This method is not supported.
+ ///
+ public void RemoveAt(int index)
+ {
+ throw new NotSupportedException();
+ }
+
+ #endregion
+
+ #region ICollection Members
+
+ ///
+ /// Clears the buffer.
+ ///
+ public void Clear()
+ {
+ // Just set the number of elements to zero.
+ _count = 0;
+ }
+
+ ///
+ /// Gets whether the buffer contains the specified element.
+ ///
+ /// The element to search for.
+ /// Whether the element is present.
+ public bool Contains(T item)
+ {
+ return this.IndexOf(item) != -1;
+ }
+
+ ///
+ /// Copies the elements of the buffer to the specified array.
+ ///
+ /// The array to copy to.
+ /// The index of the destination array at which to begin copying.
+ public void CopyTo(T[] array, int arrayIndex)
+ {
+ /*
+ * We have to make sure we don't copy unused elements.
+ * [2] [1] [0] [ ] [ ] [ ] [6] [5] [4] [3]
+ * [ . head . ] [ ... tail ... ]
+ * ^ _index (6)
+ * tailSize = _size - _index = 10 - 6 = 4
+ * headSize = _count - tailSize = 7 - 4 = 3
+ */
+ int tailSize = _size - _index;
+ int headSize = _count - tailSize;
+
+ // Copy the tail, then the head.
+ Array.Copy(_data, _index, array, arrayIndex, tailSize);
+ Array.Copy(_data, 0, array, arrayIndex + tailSize, headSize);
+ }
+
+ ///
+ /// Gets whether the buffer is read-only.
+ ///
+ public bool IsReadOnly
+ {
+ get { return false; }
+ }
+
+ ///
+ /// This method is not supported.
+ ///
+ public bool Remove(T item)
+ {
+ throw new NotSupportedException();
+ }
+
+ #endregion
+
+ #region IEnumerable Members
+
+ ///
+ /// Gets an enumerator for the buffer.
+ ///
+ public IEnumerator GetEnumerator()
+ {
+ for (int i = 0; i < this.Count; i++)
+ yield return this[i];
+ }
+
+ #endregion
+
+ #region IEnumerable Members
+
+ ///
+ /// Gets an enumerator for the buffer.
+ ///
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
+ {
+ for (int i = 0; i < this.Count; i++)
+ yield return this[i];
+ }
+
+ #endregion
+ }
+}
diff --git a/branches/ph-plugins/ProcessHacker.Common/Delegates.cs b/branches/ph-plugins/ProcessHacker.Common/Delegates.cs
new file mode 100644
index 000000000..24862dd46
--- /dev/null
+++ b/branches/ph-plugins/ProcessHacker.Common/Delegates.cs
@@ -0,0 +1,19 @@
+namespace System
+{
+ public delegate void Action();
+ //public delegate void Action(T a1);
+ public delegate void Action(T a1, U a2);
+ public delegate void Action(T a1, U a2, V a3);
+ public delegate void Action(T a1, U a2, V a3, W a4);
+ public delegate void Action(T a1, U a2, V a3, W a4, X a5);
+ public delegate void Action(T a1, U a2, V a3, W a4, X a5, Y a6);
+ public delegate void Action(T a1, U a2, V a3, W a4, X a5, Y a6, Z a7);
+
+ public delegate T Func();
+ public delegate U Func(T a1);
+ public delegate V Func(T a1, U a2);
+ public delegate W Func(T a1, U a2, V a3);
+ public delegate X Func(T a1, U a2, V a3, W a4);
+ public delegate Y Func(T a1, U a2, V a3, W a4, X a5);
+ public delegate Z Func(T a1, U a2, V a3, W a4, X a5, Y a6);
+}
diff --git a/branches/ph-plugins/ProcessHacker.Common/DeltaManager.cs b/branches/ph-plugins/ProcessHacker.Common/DeltaManager.cs
new file mode 100644
index 000000000..526e9fbef
--- /dev/null
+++ b/branches/ph-plugins/ProcessHacker.Common/DeltaManager.cs
@@ -0,0 +1,168 @@
+/*
+ * Process Hacker -
+ * delta manager
+ *
+ * Copyright (C) 2008 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.Collections.Generic;
+
+namespace ProcessHacker.Common
+{
+ ///
+ /// Defines subtraction for a numeric type.
+ ///
+ /// The numeric type.
+ public interface ISubtractor
+ {
+ ///
+ /// Subtracts v2 from v1, i.e., v1 - v2.
+ ///
+ T Subtract(T v1, T v2);
+ }
+
+ public static class Subtractor
+ {
+ private static Int64Subtractor _int64Subtractor = new Int64Subtractor();
+ private static Int32Subtractor _int32Subtractor = new Int32Subtractor();
+ private static DoubleSubtractor _doubleSubtractor = new DoubleSubtractor();
+ private static FloatSubtractor _floatSubtractor = new FloatSubtractor();
+
+ public static Int64Subtractor Int64Subtractor
+ {
+ get { return _int64Subtractor; }
+ }
+
+ public static Int32Subtractor Int32Subtractor
+ {
+ get { return _int32Subtractor; }
+ }
+
+ public static DoubleSubtractor DoubleSubtractor
+ {
+ get { return _doubleSubtractor; }
+ }
+
+ public static FloatSubtractor FloatSubtractor
+ {
+ get { return _floatSubtractor; }
+ }
+ }
+
+ ///
+ /// Provides subtraction for 64-bit integers.
+ ///
+ public class Int64Subtractor : ISubtractor
+ {
+ public long Subtract(long v1, long v2)
+ {
+ return v1 - v2;
+ }
+ }
+
+ ///
+ /// Provides subtraction for 32-bit integers.
+ ///
+ public class Int32Subtractor : ISubtractor
+ {
+ public int Subtract(int v1, int v2)
+ {
+ return v1 - v2;
+ }
+ }
+
+ ///
+ /// Provides subtraction for double-precision floating-point values.
+ ///
+ public class DoubleSubtractor : ISubtractor
+ {
+ public double Subtract(double v1, double v2)
+ {
+ return v1 - v2;
+ }
+ }
+
+ ///
+ /// Provides subtraction for single-precision floating-point values.
+ ///
+ public class FloatSubtractor : ISubtractor
+ {
+ public float Subtract(float v1, float v2)
+ {
+ return v1 - v2;
+ }
+ }
+
+ ///
+ /// Provides methods for managing deltas of discrete sets of data.
+ ///
+ public sealed class DeltaManager
+ {
+ private Dictionary _values;
+ private Dictionary _deltas;
+ private ISubtractor _subtractor;
+
+ ///
+ /// Creates a delta manager using the specified subtractor.
+ ///
+ /// A subtractor for the appropriate type.
+ public DeltaManager(ISubtractor subtractor)
+ {
+ _subtractor = subtractor;
+ _values = new Dictionary();
+ _deltas = new Dictionary();
+ }
+
+ public DeltaManager(ISubtractor subtractor, IEqualityComparer comparer)
+ {
+ _subtractor = subtractor;
+ _values = new Dictionary(comparer);
+ _deltas = new Dictionary(comparer);
+ }
+
+ public TValue this[TKey key]
+ {
+ get { return _deltas[key]; }
+ set { _deltas[key] = value; }
+ }
+
+ public TValue GetDelta(TKey key)
+ {
+ return _deltas[key];
+ }
+
+ public void Add(TKey key, TValue initialValue)
+ {
+ _values.Add(key, initialValue);
+ _deltas.Add(key, initialValue);
+ }
+
+ public void SetDelta(TKey key, TValue value)
+ {
+ _deltas[key] = value;
+ }
+
+ public TValue Update(TKey key, TValue value)
+ {
+ _deltas[key] = _subtractor.Subtract(value, _values[key]);
+ _values[key] = value;
+
+ return _deltas[key];
+ }
+ }
+}
diff --git a/branches/ph-plugins/ProcessHacker.Common/EnumComparer.cs b/branches/ph-plugins/ProcessHacker.Common/EnumComparer.cs
new file mode 100644
index 000000000..ed0aa58a5
--- /dev/null
+++ b/branches/ph-plugins/ProcessHacker.Common/EnumComparer.cs
@@ -0,0 +1,124 @@
+/*
+ * http://www.codeproject.com/KB/cs/EnumComparer.aspx
+ *
+ * by Omer Mor
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Reflection.Emit;
+
+namespace ProcessHacker.Common
+{
+ public sealed class EnumComparer : IEqualityComparer
+ where TEnum : struct, IComparable, IConvertible, IFormattable
+ {
+ public static readonly EnumComparer Instance;
+
+ private static readonly Func _equals;
+ private static readonly Func _getHashCode;
+
+ static EnumComparer()
+ {
+ _getHashCode = generateGetHashCode();
+ _equals = generateEquals();
+ Instance = new EnumComparer();
+ }
+
+ private EnumComparer()
+ {
+ AssertTypeIsEnum();
+ AssertUnderlyingTypeIsSupported();
+ }
+
+ public bool Equals(TEnum x, TEnum y)
+ {
+ return _equals(x, y);
+ }
+
+ public int GetHashCode(TEnum obj)
+ {
+ return _getHashCode(obj);
+ }
+
+ private static void AssertTypeIsEnum()
+ {
+ if (typeof(TEnum).IsEnum)
+ return;
+
+ throw new NotSupportedException();
+ }
+
+ private static void AssertUnderlyingTypeIsSupported()
+ {
+ var underlyingType = Enum.GetUnderlyingType(typeof(TEnum));
+ ICollection supportedTypes =
+ new[]
+ {
+ typeof (byte), typeof (sbyte), typeof (short), typeof (ushort),
+ typeof (int), typeof (uint), typeof (long), typeof (ulong)
+ };
+
+ if (supportedTypes.Contains(underlyingType))
+ return;
+
+ throw new NotSupportedException();
+ }
+
+ ///
+ /// Generates a comparison method similar to this:
+ ///
+ /// bool Equals(TEnum x, TEnum y)
+ /// {
+ /// return x == y;
+ /// }
+ ///
+ ///
+ /// The generated method.
+ private static Func generateEquals()
+ {
+ var method = new DynamicMethod(typeof(TEnum).Name + "_Equals",
+ typeof(bool),
+ new[] { typeof(TEnum), typeof(TEnum) },
+ typeof(TEnum), true);
+ var generator = method.GetILGenerator();
+ // Writing body
+ generator.Emit(OpCodes.Ldarg_0); // load x to stack
+ generator.Emit(OpCodes.Ldarg_1); // load y to stack
+ generator.Emit(OpCodes.Ceq); // x == y
+ generator.Emit(OpCodes.Ret); // return result
+ return (Func)method.CreateDelegate
+ (typeof(Func));
+ }
+
+ ///
+ /// Generates a GetHashCode method similar to this:
+ ///
+ /// int GetHashCode(TEnum obj)
+ /// {
+ /// return ((int)obj).GetHashCode();
+ /// }
+ ///
+ ///
+ /// The generated method.
+ private static Func generateGetHashCode()
+ {
+ var method = new DynamicMethod(typeof(TEnum).Name + "_GetHashCode",
+ typeof(int),
+ new[] { typeof(TEnum) },
+ typeof(TEnum), true);
+ var generator = method.GetILGenerator();
+ var underlyingType = Enum.GetUnderlyingType(typeof(TEnum));
+ var getHashCodeMethod = underlyingType.GetMethod("GetHashCode");
+ var castValue = generator.DeclareLocal(underlyingType);
+ // Writing body
+ generator.Emit(OpCodes.Ldarg_0); // load obj to stack
+ generator.Emit(OpCodes.Stloc_0); // castValue = obj
+ generator.Emit(OpCodes.Ldloca_S, castValue); // load *castValue to stack
+ generator.Emit(OpCodes.Call, getHashCodeMethod); // castValue.GetHashCode()
+ generator.Emit(OpCodes.Ret); // return result
+ return (Func)method.CreateDelegate(typeof(Func));
+ }
+ }
+}
diff --git a/branches/ph-plugins/ProcessHacker.Common/ExtensionAttribute.cs b/branches/ph-plugins/ProcessHacker.Common/ExtensionAttribute.cs
new file mode 100644
index 000000000..0e39f6c55
--- /dev/null
+++ b/branches/ph-plugins/ProcessHacker.Common/ExtensionAttribute.cs
@@ -0,0 +1,6 @@
+namespace System.Runtime.CompilerServices
+{
+ [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
+ public class ExtensionAttribute : Attribute
+ { }
+}
diff --git a/branches/ph-plugins/ProcessHacker.Common/FreeList.cs b/branches/ph-plugins/ProcessHacker.Common/FreeList.cs
new file mode 100644
index 000000000..6b2588b30
--- /dev/null
+++ b/branches/ph-plugins/ProcessHacker.Common/FreeList.cs
@@ -0,0 +1,126 @@
+/*
+ * Process Hacker -
+ * free list
+ *
+ * Copyright (C) 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.Threading;
+
+namespace ProcessHacker.Common
+{
+ ///
+ /// Manages a list of free objects that can be re-used.
+ ///
+ public class FreeList
+ where T : IResettable, new()
+ {
+ private class FreeListEntry
+ where U : IResettable, new()
+ {
+ public U Object;
+ public FreeListEntry Next;
+ }
+
+ private FreeListEntry _listHead = null;
+ private int _count = 0;
+ private int _maximumCount = 0;
+
+ public int Count
+ {
+ get { return _count; }
+ }
+
+ public int MaximumCount
+ {
+ get { return _maximumCount; }
+ set { _maximumCount = value; }
+ }
+
+ public T Allocate()
+ {
+ FreeListEntry listHead;
+
+ // Atomically pop an entry off and replace the list head
+ // pointer with a pointer to the next entry.
+ while (true)
+ {
+ listHead = _listHead;
+
+ // If the list head pointer is null, we don't have anything
+ // to use from the free list.
+ if (listHead == null)
+ break;
+
+ // Try to replace the list head pointer.
+ if (Interlocked.CompareExchange>(
+ ref _listHead,
+ listHead.Next,
+ listHead
+ ) == listHead)
+ {
+ // Success.
+ _count--;
+ return listHead.Object;
+ }
+ }
+
+ return this.AllocateNew();
+ }
+
+ private T AllocateNew()
+ {
+ T obj = new T();
+ obj.ResetObject();
+ return obj;
+ }
+
+ public void Free(T obj)
+ {
+ FreeListEntry listHead;
+ FreeListEntry listEntry;
+
+ // Add the object to the free list if we haven't
+ // exceeded the maximum count.
+ if (_count < _maximumCount || _maximumCount == 0)
+ {
+ listEntry = new FreeListEntry();
+
+ listEntry.Object = obj;
+
+ // Atomically add the list entry.
+ while (true)
+ {
+ listHead = _listHead;
+ listEntry.Next = listHead;
+
+ if (Interlocked.CompareExchange>(
+ ref _listHead,
+ listEntry,
+ listHead
+ ) == listHead)
+ {
+ // Success.
+ _count++;
+ break;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/branches/ph-plugins/ProcessHacker.Common/HistoryManager.cs b/branches/ph-plugins/ProcessHacker.Common/HistoryManager.cs
new file mode 100644
index 000000000..a7db51960
--- /dev/null
+++ b/branches/ph-plugins/ProcessHacker.Common/HistoryManager.cs
@@ -0,0 +1,104 @@
+/*
+ * Process Hacker -
+ * history manager
+ *
+ * Copyright (C) 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.Collections.ObjectModel;
+using System.Text;
+
+namespace ProcessHacker.Common
+{
+ public static class HistoryManager
+ {
+ private static int _globalMaxCount = 600;
+
+ public static int GlobalMaxCount
+ {
+ get { return _globalMaxCount; }
+ set { _globalMaxCount = value; }
+ }
+ }
+
+ public sealed class HistoryManager
+ {
+ private Dictionary> _history;
+ private Dictionary> _readOnlyCollections;
+ private int _maxCount = -1;
+
+ public HistoryManager()
+ {
+ _history = new Dictionary>();
+ _readOnlyCollections = new Dictionary>();
+ }
+
+ public HistoryManager(IEqualityComparer comparer)
+ {
+ _history = new Dictionary>(comparer);
+ _readOnlyCollections = new Dictionary>(comparer);
+ }
+
+ public int MaxCount
+ {
+ get { return _maxCount; }
+ set { _maxCount = value; }
+ }
+
+ public int EffectiveMaxCount
+ {
+ get { return _maxCount == -1 ? HistoryManager.GlobalMaxCount : _maxCount; }
+ }
+
+ public ReadOnlyCollection this[TKey key]
+ {
+ get { return GetHistory(key); }
+ }
+
+ public void Add(TKey key)
+ {
+ _history.Add(key, new CircularBuffer(this.EffectiveMaxCount));
+ }
+
+ public ReadOnlyCollection GetHistory(TKey key)
+ {
+ if (!_readOnlyCollections.ContainsKey(key))
+ {
+ lock (_readOnlyCollections)
+ {
+ if (!_readOnlyCollections.ContainsKey(key))
+ _readOnlyCollections.Add(key, new ReadOnlyCollection(_history[key]));
+ }
+ }
+
+ return _readOnlyCollections[key];
+ }
+
+ public void Update(TKey key, TValue value)
+ {
+ int maxCount = this.EffectiveMaxCount;
+
+ if (_history[key].Size != maxCount)
+ _history[key].Resize(maxCount);
+
+ _history[key].Add(value);
+ }
+ }
+}
diff --git a/branches/ph-plugins/ProcessHacker.Common/IResettable.cs b/branches/ph-plugins/ProcessHacker.Common/IResettable.cs
new file mode 100644
index 000000000..2fa07049c
--- /dev/null
+++ b/branches/ph-plugins/ProcessHacker.Common/IResettable.cs
@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProcessHacker.Common
+{
+ public interface IResettable
+ {
+ void ResetObject();
+ }
+}
diff --git a/branches/ph-plugins/ProcessHacker.Common/IdGenerator.cs b/branches/ph-plugins/ProcessHacker.Common/IdGenerator.cs
new file mode 100644
index 000000000..cae77136d
--- /dev/null
+++ b/branches/ph-plugins/ProcessHacker.Common/IdGenerator.cs
@@ -0,0 +1,116 @@
+/*
+ * Process Hacker -
+ * unique ID generator
+ *
+ * Copyright (C) 2008 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;
+
+namespace ProcessHacker.Common
+{
+ ///
+ /// Provides a facility for generating unique IDs.
+ ///
+ public class IdGenerator
+ {
+ private int _step = 1;
+ private bool _sort = false;
+ private List _ids = new List();
+ private int _id;
+
+ ///
+ /// Creates a new ID generator.
+ ///
+ public IdGenerator()
+ : this(0)
+ { }
+
+ ///
+ /// Creates a new ID generator.
+ ///
+ /// The starting ID.
+ public IdGenerator(int start)
+ : this(start, 1)
+ { }
+
+ ///
+ /// Creates a new ID generator.
+ ///
+ /// The starting ID.
+ /// The number each ID will be divisible by.
+ public IdGenerator(int start, int step)
+ {
+ if (step == 0)
+ throw new ArgumentException("step cannot be zero.");
+
+ _id = start;
+ _step = step;
+ }
+
+ public bool Sort
+ {
+ get { return _sort; }
+ set { _sort = value; }
+ }
+
+ ///
+ /// Generates a new ID.
+ ///
+ ///
+ public int Pop()
+ {
+ int id;
+
+ lock (_ids)
+ {
+ if (_ids.Count > 0)
+ {
+ id = _ids[0];
+
+ _ids.Remove(_ids[0]);
+
+ return id;
+ }
+ else
+ {
+ id = _id;
+ _id += _step;
+ }
+ }
+
+ return id;
+ }
+
+ ///
+ /// Makes an ID available for use.
+ ///
+ ///
+ public void Push(int id)
+ {
+ lock (_ids)
+ {
+ _ids.Add(id);
+
+ if (_sort)
+ _ids.Sort();
+ }
+ }
+ }
+}
diff --git a/branches/ph-plugins/ProcessHacker.Common/Linq/GroupedEnumerable.cs b/branches/ph-plugins/ProcessHacker.Common/Linq/GroupedEnumerable.cs
new file mode 100644
index 000000000..d9b935a1c
--- /dev/null
+++ b/branches/ph-plugins/ProcessHacker.Common/Linq/GroupedEnumerable.cs
@@ -0,0 +1,43 @@
+//using System;
+//using System.Collections.Generic;
+//using System.Text;
+//using System.Collections;
+
+//namespace System.Linq
+//{
+// internal class GroupedEnumerable : IEnumerable>, IEnumerable
+// {
+// IEnumerable _source;
+// Func _keySelector;
+// Func _elementSelector;
+// IEqualityComparer _comparer;
+
+// // Methods
+// public GroupedEnumerable (
+// IEnumerable source,
+// Func keySelector,
+// Func elementSelector,
+// IEqualityComparer comparer)
+// {
+// if (source == null) throw new ArgumentNullException ("source");
+// if (keySelector == null) throw new ArgumentNullException ("keySelector");
+// if (elementSelector == null) throw new ArgumentNullException ("elementSelector");
+
+// _source = source;
+// _keySelector = keySelector;
+// _elementSelector = elementSelector;
+// _comparer = comparer;
+// }
+
+// public IEnumerator> GetEnumerator ()
+// {
+// return Lookup.Create (_source, _keySelector, _elementSelector, _comparer).GetEnumerator ();
+// }
+
+// IEnumerator IEnumerable.GetEnumerator ()
+// {
+// return this.GetEnumerator ();
+// }
+// }
+
+//}
diff --git a/branches/ph-plugins/ProcessHacker.Common/Linq/Grouping.cs b/branches/ph-plugins/ProcessHacker.Common/Linq/Grouping.cs
new file mode 100644
index 000000000..836f9cc26
--- /dev/null
+++ b/branches/ph-plugins/ProcessHacker.Common/Linq/Grouping.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Collections.ObjectModel;
+
+namespace System.Linq
+{
+ internal class Grouping : ReadOnlyCollection, IGrouping
+ {
+ internal IList InnerList { get { return this.Items; } }
+
+ public TKey Key { get; private set; }
+
+ public Grouping (TKey key) : base (new List())
+ {
+ Key = key;
+ }
+ }
+}
diff --git a/branches/ph-plugins/ProcessHacker.Common/Linq/IGrouping.cs b/branches/ph-plugins/ProcessHacker.Common/Linq/IGrouping.cs
new file mode 100644
index 000000000..2bb7a8438
--- /dev/null
+++ b/branches/ph-plugins/ProcessHacker.Common/Linq/IGrouping.cs
@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace System.Linq
+{
+ public interface IGrouping : IEnumerable
+ {
+ TKey Key { get; }
+ }
+}
diff --git a/branches/ph-plugins/ProcessHacker.Common/Linq/ILookup.cs b/branches/ph-plugins/ProcessHacker.Common/Linq/ILookup.cs
new file mode 100644
index 000000000..00ab824b5
--- /dev/null
+++ b/branches/ph-plugins/ProcessHacker.Common/Linq/ILookup.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace System.Linq
+{
+ public interface ILookup : IEnumerable>
+ {
+ int Count { get; }
+ bool Contains (TKey key);
+ IEnumerable this [TKey key] { get; }
+ }
+
+}
\ No newline at end of file
diff --git a/branches/ph-plugins/ProcessHacker.Common/Linq/IOrderedEnumerable.cs b/branches/ph-plugins/ProcessHacker.Common/Linq/IOrderedEnumerable.cs
new file mode 100644
index 000000000..4e466b336
--- /dev/null
+++ b/branches/ph-plugins/ProcessHacker.Common/Linq/IOrderedEnumerable.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace System
+{
+ // We'll stick with Microsoft's definition of IOrderedEnumerable to minimize confusion.
+
+ public interface IOrderedEnumerable : IEnumerable
+ {
+ IOrderedEnumerable CreateOrderedEnumerable (
+ Func keySelector,
+ IComparer comparer,
+ bool descending);
+ }
+}
diff --git a/branches/ph-plugins/ProcessHacker.Common/Linq/License.txt b/branches/ph-plugins/ProcessHacker.Common/Linq/License.txt
new file mode 100644
index 000000000..e440b797d
--- /dev/null
+++ b/branches/ph-plugins/ProcessHacker.Common/Linq/License.txt
@@ -0,0 +1,20 @@
+LINQBridge Copyright (c) 2007-2008 Joseph Albahari
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
diff --git a/branches/ph-plugins/ProcessHacker.Common/Linq/Lookup.cs b/branches/ph-plugins/ProcessHacker.Common/Linq/Lookup.cs
new file mode 100644
index 000000000..d91e08968
--- /dev/null
+++ b/branches/ph-plugins/ProcessHacker.Common/Linq/Lookup.cs
@@ -0,0 +1,70 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Collections;
+
+namespace System.Linq
+{
+ public class Lookup : ILookup
+ {
+ Dictionary> _groupings;
+
+ internal static Lookup Create (
+ IEnumerable source,
+ Func keySelector,
+ Func elementSelector,
+ IEqualityComparer comparer)
+ {
+ if (source == null) throw new ArgumentNullException ("source");
+ if (keySelector == null) throw new ArgumentNullException ("keySelector");
+ if (elementSelector == null) throw new ArgumentNullException ("elementSelector");
+
+ var lookup = new Lookup (comparer ?? EqualityComparer.Default);
+
+ foreach (TSource element in source)
+ {
+ TKey key = keySelector (element);
+ Grouping grouping;
+
+ if (!lookup._groupings.TryGetValue (key, out grouping))
+ lookup._groupings.Add (key, grouping = new Grouping (key));
+
+ grouping.InnerList.Add (elementSelector (element));
+ }
+
+ return lookup;
+ }
+
+ Lookup (IEqualityComparer comparer)
+ {
+ _groupings = new Dictionary> (comparer);
+ }
+
+ public int Count { get { return _groupings.Count; } }
+
+ public IEnumerable this [TKey key]
+ {
+ get
+ {
+ Grouping result;
+ if (_groupings.TryGetValue (key, out result))
+ return result;
+ else
+ return Enumerable.Empty ();
+ }
+ }
+
+ public bool Contains (TKey key)
+ {
+ return _groupings.ContainsKey (key);
+ }
+
+ public IEnumerator> GetEnumerator ()
+ {
+ foreach (var grouping in _groupings.Values)
+ yield return grouping;
+ }
+
+ IEnumerator IEnumerable.GetEnumerator () { return GetEnumerator (); }
+ }
+}
diff --git a/branches/ph-plugins/ProcessHacker.Common/Linq/OrderByEnumerable.cs b/branches/ph-plugins/ProcessHacker.Common/Linq/OrderByEnumerable.cs
new file mode 100644
index 000000000..ec2d16dec
--- /dev/null
+++ b/branches/ph-plugins/ProcessHacker.Common/Linq/OrderByEnumerable.cs
@@ -0,0 +1,58 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace System.Linq
+{
+ class OrderByEnumerable : IOrderedEnumerable
+ {
+ public readonly IEnumerable Source;
+ public readonly Func KeySelector;
+ public readonly IComparer Comparer;
+ public readonly bool Descending;
+
+ public OrderByEnumerable (IEnumerable source, Func keySelector, IComparer comparer, bool descending)
+ {
+ if (source == null) throw new ArgumentNullException ("source");
+ if (keySelector == null) throw new ArgumentNullException ("keySelector");
+
+ Source = source;
+ KeySelector = keySelector;
+ Comparer = comparer ?? Comparer.Default;
+ Descending = descending;
+ }
+
+ public IOrderedEnumerable CreateOrderedEnumerable (
+ Func keySelector,
+ IComparer comparer,
+ bool descending)
+ {
+ return new ThenByEnumerable (this, keySelector, comparer, descending);
+ }
+
+ internal virtual int CompareElements (TElement e1, TElement e2) // ThenByEnumerable will override this method.
+ {
+ int result = Comparer.Compare (KeySelector (e1), KeySelector (e2));
+ return Descending ? -result : result;
+ }
+
+ internal virtual IEnumerable GetElementsToSort () // ThenByEnumerable will override this method.
+ {
+ return Source;
+ }
+
+ public IEnumerator GetEnumerator ()
+ {
+ TElement [] array = GetElementsToSort ().ToArray();
+ Array.Sort (array, CompareElements);
+ foreach (TElement element in array)
+ yield return element;
+ }
+
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator ()
+ {
+ return GetEnumerator();
+ }
+ }
+
+}
diff --git a/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Aggregation Operators - Average.cs b/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Aggregation Operators - Average.cs
new file mode 100644
index 000000000..571384bbc
--- /dev/null
+++ b/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Aggregation Operators - Average.cs
@@ -0,0 +1,147 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace System.Linq
+{
+ public static partial class Enumerable
+ {
+ // int
+
+ public static double Average (this IEnumerable source)
+ {
+ if (source == null) throw new ArgumentNullException ("source");
+ return (double) source.Sum () / source.Count ();
+ }
+
+ public static double? Average (this IEnumerable source)
+ {
+ if (source == null) throw new ArgumentNullException ("source");
+
+ int count = source.Count();
+ if (count == 0) return null;
+ return (double)source.Sum () / count;
+ }
+
+ public static double Average (this IEnumerable source, Func selector)
+ {
+ return source.Select (selector).Average ();
+ }
+
+ public static double? Average (this IEnumerable source, Func selector)
+ {
+ return source.Select (selector).Average ();
+ }
+
+ // long
+
+ public static double Average (this IEnumerable source)
+ {
+ if (source == null) throw new ArgumentNullException ("source");
+ return (double)source.Sum () / source.Count ();
+ }
+
+ public static double? Average (this IEnumerable source)
+ {
+ if (source == null) throw new ArgumentNullException ("source");
+
+ int count = source.Count ();
+ if (count == 0) return null;
+ return (double)source.Sum () / count;
+ }
+
+ public static double Average (this IEnumerable source, Func selector)
+ {
+ return source.Select (selector).Average ();
+ }
+
+ public static double? Average (this IEnumerable source, Func selector)
+ {
+ return source.Select (selector).Average ();
+ }
+
+ // float
+
+ public static float Average (this IEnumerable source)
+ {
+ if (source == null) throw new ArgumentNullException ("source");
+ return source.Sum () / source.Count ();
+ }
+
+ public static float? Average (this IEnumerable source)
+ {
+ if (source == null) throw new ArgumentNullException ("source");
+
+ int count = source.Count ();
+ if (count == 0) return null;
+ return source.Sum () / count;
+ }
+
+ public static float Average (this IEnumerable source, Func selector)
+ {
+ return source.Select (selector).Average ();
+ }
+
+ public static float? Average (this IEnumerable source, Func selector)
+ {
+ return source.Select (selector).Average ();
+ }
+
+ // double
+
+ public static double Average (this IEnumerable source)
+ {
+ if (source == null) throw new ArgumentNullException ("source");
+ return source.Sum () / source.Count ();
+ }
+
+ public static double? Average (this IEnumerable source)
+ {
+ if (source == null) throw new ArgumentNullException ("source");
+
+ int count = source.Count ();
+ if (count == 0) return null;
+ return source.Sum () / count;
+ }
+
+ public static double Average (this IEnumerable source, Func selector)
+ {
+ return source.Select (selector).Average ();
+ }
+
+ public static double? Average (this IEnumerable source, Func selector)
+ {
+ return source.Select (selector).Average ();
+ }
+
+ // decimal
+
+ public static decimal Average (this IEnumerable source)
+ {
+ if (source == null) throw new ArgumentNullException ("source");
+
+ int count = source.Count ();
+ if (count == 0) ThrowNoElements (); // decimal has no special "NaN" value, so we can't divide by zero.
+ return source.Sum () / count;
+ }
+
+ public static decimal? Average (this IEnumerable source)
+ {
+ if (source == null) throw new ArgumentNullException ("source");
+
+ int count = source.Count ();
+ if (count == 0) return null;
+ return source.Sum () / count;
+ }
+
+ public static decimal Average (this IEnumerable source, Func selector)
+ {
+ return source.Select (selector).Average ();
+ }
+
+ public static decimal? Average (this IEnumerable source, Func selector)
+ {
+ return source.Select (selector).Average ();
+ }
+ }
+}
diff --git a/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Aggregation Operators - Sum.cs b/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Aggregation Operators - Sum.cs
new file mode 100644
index 000000000..bc82e92dc
--- /dev/null
+++ b/branches/ph-plugins/ProcessHacker.Common/Linq/Query Operators/Aggregation Operators - Sum.cs
@@ -0,0 +1,162 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace System.Linq
+{
+ public static partial class Enumerable
+ {
+ // int
+
+ public static int Sum (this IEnumerable source)
+ {
+ if (source == null) throw new ArgumentNullException ("source");
+
+ int tot = 0;
+ foreach (int element in source) checked { tot += element; };
+ return tot;
+ }
+
+ // It makes no sense to me that this returns a double? rather than a double, but that's the way the standard query operators work.
+ public static int? Sum (this IEnumerable source)
+ {
+ if (source == null) throw new ArgumentNullException ("source");
+
+ int tot = 0;
+ foreach (int? element in source) checked { tot += element ?? 0; };
+ return tot;
+ }
+
+ public static int Sum (this IEnumerable source, Func selector)
+ {
+ return source.Select (selector).Sum ();
+ }
+
+ public static int? Sum (this IEnumerable source, Func selector)
+ {
+ return source.Select (selector).Sum ();
+ }
+
+ // long
+
+ public static long Sum (this IEnumerable source)
+ {
+ if (source == null) throw new ArgumentNullException ("source");
+
+ long tot = 0;
+ foreach (long element in source) checked { tot += element; }
+ return tot;
+ }
+
+ public static long? Sum (this IEnumerable source)
+ {
+ if (source == null) throw new ArgumentNullException ("source");
+
+ long tot = 0;
+ foreach (long? element in source) checked { tot += element ?? 0; }
+ return tot;
+ }
+
+ public static long Sum (this IEnumerable source, Func selector)
+ {
+ return source.Select (selector).Sum ();
+ }
+
+ public static long? Sum (this IEnumerable source, Func selector)
+ {
+ return source.Select (selector).Sum ();
+ }
+
+ // float
+
+ public static float Sum (this IEnumerable source)
+ {
+ if (source == null) throw new ArgumentNullException ("source");
+
+ float tot = 0;
+ foreach (float element in source) tot += element;
+ return tot;
+ }
+
+ public static float? Sum (this IEnumerable source)
+ {
+ if (source == null) throw new ArgumentNullException ("source");
+
+ float tot = 0;
+ foreach (float? element in source) tot += element ?? 0;
+ return tot;
+ }
+
+ public static float Sum (this IEnumerable source, Func selector)
+ {
+ return source.Select (selector).Sum ();
+ }
+
+ public static float? Sum (this IEnumerable source, Func selector)
+ {
+ return source.Select (selector).Sum ();
+ }
+
+ // double
+
+ public static double Sum (this IEnumerable source)
+ {
+ if (source == null) throw new ArgumentNullException ("source");
+
+ double tot = 0;
+ foreach (double element in source) tot += element;
+ return tot;
+ }
+
+ public static double? Sum (this IEnumerable source)
+ {
+ if (source == null) throw new ArgumentNullException ("source");
+
+ double tot = 0;
+ foreach (double? element in source)
+ tot += element ?? 0;
+ return tot;
+ }
+
+ public static double Sum (this IEnumerable source, Func selector)
+ {
+ return source.Select (selector).Sum ();
+ }
+
+ public static double ?Sum (this IEnumerable source, Func selector)
+ {
+ return source.Select (selector).Sum ();
+ }
+
+ // decimal
+
+ public static decimal Sum (this IEnumerable source)
+ {
+ if (source == null) throw new ArgumentNullException ("source");
+
+ decimal tot = 0;
+ foreach (decimal element in source) tot += element;
+ return tot;
+ }
+
+ public static decimal? Sum (this IEnumerable source)
+ {
+ if (source == null) throw new ArgumentNullException ("source");
+
+ decimal tot = 0;
+ foreach (decimal? element in source)
+ tot += element ?? 0;
+ return tot;
+ }
+
+ public static decimal Sum (this IEnumerable