Solved

Launch Process with NOT as Administrator

Posted on 2016-10-18
15
64 Views
Last Modified: 2016-10-28
Is it possible, in Windows 10, for an application WITH Administrator Permissions to launch a process WITHOUT Administrator Permissions?

I'm using C#.  To keep the discussion straightforward, let's say I launch Visual Studio with Administrator Permissions (dvenv.exe).  Here is the code:

          System.Diagnostics.Process.Start(@"C:\Windows\System32\cmd.exe");

cmd is always launched with "Run as administrator".

If I launch Visual Studio Not as Admnistrator, cmd is launched Not as Administrator.

Is there a way for dvenv.exe when launched As Administrator to launch cmd NOT as administrator?

Thanks in advance.
0
Comment
Question by:richtelieu88
  • 7
  • 6
  • 2
15 Comments
 
LVL 53

Expert Comment

by:McKnife
Comment Utility
Sure, simply use psexec or runas.
0
 
LVL 32

Expert Comment

by:it_saige
Comment Utility
You are wanting to de-elevate the child process from the parent process.  One way this can be accomplished is by using Explorer.exe to launch the process:

http://mdb-blog.blogspot.com/2013/01/nsis-lunch-program-as-user-from-uac.html
https://blogs.msdn.microsoft.com/oldnewthing/20131118-00/?p=2643

Unfortunately, this is not a bullet-proof solution as this uses a bug which the Windows Shell team has said will be addressed and may not work in future updates/versions of Windows.

The next viable solution is to use the Username and Password properties of the ProcessStartInfo class in order to specify the credentials to launch the process as.

And one more solution is to use code injection to start your process:

http://www.codeproject.com/Articles/18946/High-elevation-can-be-bad-for-your-application-How

-saige-
0
 
LVL 32

Expert Comment

by:it_saige
Comment Utility
Seems like someone created some User Account Control Helper methods.  Because this library was written in 2009, i cannot guarantee the codes current relevance nor reliability.

http://uachelpers.codeplex.com/

-saige-
0
 

Author Comment

by:richtelieu88
Comment Utility
I'm trying:
          ProcessStartInfo info = new ProcessStartInfo();
          info.WorkingDirectory = @"C:\Windows\System32";
          info.FileName = @"cmd.exe";
          info.UseShellExecute = false;
          info.CreateNoWindow = false;
          . . .
          Process whatever = Process.Start( info );

"McKnife", this does not work:
          info.Verb = "run as";

Am I supposed to put something other than simply "run as"?

"it_saige", this doesn't work, probably because I have administrator permissions:
          info.Verb = "runas /user: [MY USERNAME]";

I'll take a look at the other links provided.
0
 
LVL 53

Expert Comment

by:McKnife
Comment Utility
Sorry, I don't know c sharp. But since we can launch a program from an elevated command prompt with different (non-admin) credentials using runas or psexec, I am sure, there will be a way.
0
 
LVL 32

Expert Comment

by:it_saige
Comment Utility
The "runas" verb tells the process to run as an elevated user, the proper usage is without spaces; e.g. -
ProcessStartInfo info = new ProcessStartInfo() { Verb = "runas" };

Open in new window

This does not help with your request because you want to de-elevate the process, not elevate it.  As for the username and password, these are also properties of the ProcessStartInfo class.  An example of their proper usage is:
ProcessStartInfo info = new ProcessStartInfo() { UserName = "SomeUserName", Password = "SomePassword" };

Open in new window


-saige-
0
 

Author Comment

by:richtelieu88
Comment Utility
"it_saige",

I am trying your "code injection" recommendation.

It works fine when launching VS NOT As Administrator.

However, when I exit VS, then relaunch VS *AS Administrator*, it does nothing . . . it does not launch cmd.exe.

To see what I'm seeing, create a new C# Windows application project.

Code the Form's Load() function as:

      private void Form1_Load(object sender, EventArgs e)
        {
                string sVista, sCmd;
                string sTotal;

                sVista = @"C:\[LOCATION OF YOUR NEW WINDOWS C# PROJECT]\WindowsFormsApplication1\bin\Debug\VistaLib32.dll";

                sCmd = @"C:\WINDOWS\system32\cmd.exe";

                sTotal = [DOUBLE QUOTE] + sVista + [DOUBLE QUOTE] + "," + "RunNonElevated" + " " + [DOUBLE QUOTE] + sCmd + [DOUBLE QUOTE];

                ProcessStartInfo Prog = new ProcessStartInfo();
                Prog.UseShellExecute = true;
                Prog.FileName = @"C:\WINDOWS\system32\RUNDLL32.EXE";
                Prog.Arguments = sTotal;
                Process p = Process.Start(Prog);

        }

Where:
          [LOCATION OF YOUR NEW WINDOWS C# PROJECT]   is the location of your new C# project.

          [DOUBLE QUOTE] refers to a function I wrote which simply returns a double quote character.

This should result in "sTotal" looking like this in the debugger:

sTotal      "\"C:\\[LOCATION OF YOUR NEW WINDOWS C# PROJECT]\\WindowsFormsApplication1\\bin\\Debug\\VistaLib32.dll\",RunNonElevated \"C:\\WINDOWS\\system32\\cmd.exe\""      string

Then put "VistaLib32.dll" in the same directory as the C# project's "Debug" directory.

Create a class in the C# project called, let's say, "Native.cs".

Put this in "Native.cs":

        [DllImport(@"VistaLib32.dll")]
        public static extern Boolean RunNonElevated(IntPtr hWnd,
                    string pszPath,
                    string pszParameters,
                    string pszDirectory,
                    ref IntPtr phProcess);

Change the Platform Target from "Any CPU" to "x86".

Execute this C# project.

Nothing happens.

If I can get this to work, I'll accept this as the solution.

Please help.
0
Why You Should Analyze Threat Actor TTPs

After years of analyzing threat actor behavior, it’s become clear that at any given time there are specific tactics, techniques, and procedures (TTPs) that are particularly prevalent. By analyzing and understanding these TTPs, you can dramatically enhance your security program.

 
LVL 32

Expert Comment

by:it_saige
Comment Utility
The reason why this fails to work is that you put the file into the build location for an AnyCPU build configuration and then change the configuration to an x86 build.

You can either distribute the dll with your application, by adding it to the project and setting the 'Copy to Output Directory' property to either 'Copy always' or 'Copy if newer' -Capture.JPG
Or you can build your program with the associated dll embedded and have a resolver distribute the needed dll into the application directory.  Something like this:

Create a folder in the project to contain your resources, I named my folder 'Resources' and copied the 'VistaLib32.dll' file into it -Capture.JPG
Include the 'Resources' folder (and it's contents), into your project -Capture.JPG
Select the 'VistaLib32.dll' file and change the 'Build Action' property to 'Embedded Resource' (leave the 'Copy to Output Directory' as 'Do not copy'; yes this is different than above) -Capture.JPG
After you do that, you now need a way to retrieve the embedded resource and save it to the disk;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;

/// <summary>Class EmbeddedAssembly.</summary>
class EmbeddedAssembly
{
	/// <summary>The assembly dictionary</summary>
	static Dictionary<string, byte[]> fAssemblyDictionary = null;

	/// <summary>Load Assembly, DLL from Embedded Resources into memory.</summary>
	/// <param name="resourceString">A <see cref="String"/> value that represents the embedded resource string.</param>
	/// <param name="fileName">A <see cref="String"/> value that represents the file name of the resource.</param>
	/// <example>EmbeddedAssembly.Load("WindowsFormsApplication1.SomeTools.dll", "SomeTools.dll")</example>
	public static void Load(string resourceString, string fileName)
	{
		Console.WriteLine("Attempting to load the following embedded resource {0} ({1}).", fileName, resourceString);
		if (fAssemblyDictionary == null)
			fAssemblyDictionary = new Dictionary<string, byte[]>(StringComparer.OrdinalIgnoreCase);

		byte[] buffer = null;
		var current = Assembly.GetExecutingAssembly();

		foreach (var name in current.GetManifestResourceNames())
			Console.WriteLine("Embedded resource {0} of '{1}'.", name, current.GetName());

		using (var stream = current.GetManifestResourceStream(resourceString))
		{
			// Either the file does not exist or it is not mark as an embedded resource.
			if (stream == null)
			{
				Console.WriteLine("WARNING!!!  Embedded resource {0} ({1}) was not found in '{2}'.", fileName, resourceString, current.GetName());
				return;
			}

			Console.WriteLine("Found embedded resource {0} ({1}) in '{2}'.", fileName, resourceString, current.GetName());
			// Get byte[] from the file from embedded resource
			buffer = new byte[(int)stream.Length];
			stream.Read(buffer, 0, (int)stream.Length);
			try
			{
				Console.WriteLine("Loading embedded resource {0} ({1}) from '{2}'.", fileName, resourceString, current.GetName());
				// Add the assembly/dll into dictionary
				Console.WriteLine("Adding embedded resource {0} ({1}) to local dictionary '{2}'.", fileName, resourceString, current.GetName());
				fAssemblyDictionary.Add(fileName, buffer);
				return;
			}
			// Purposely do nothing.  An unmanaged dll or assembly cannot be loaded directly from byte[].  Let the process fall through for next part
			catch { /* Eat the exception */ ;}
		}
	}

	/// <summary>Saves the specified name.</summary>
	/// <param name="name">The name.</param>
	/// <param name="target">The target.</param>
	/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
	public static bool Save(string name, string target)
	{
		bool results = false;
		try
		{
			if (!Directory.Exists(target))
				Directory.CreateDirectory(target);

			if (fAssemblyDictionary.ContainsKey(name))
				File.WriteAllBytes(Path.Combine(target, name), fAssemblyDictionary[name]);
			results = true;
		}
		catch (Exception)
		{
			results = false;
		}
		return results;
	}
}

Open in new window


Now we can put it all together -

Program.cs -
using System;
using System.IO;
using System.Windows.Forms;

namespace EE_Q28977165
{
	static class Program
	{
		static Program()
		{
			EmbeddedAssembly.Load("EE_Q28977165.Resources.VistaLib32.dll", "VistaLib32.dll");
			EmbeddedAssembly.Save("VistaLib32.dll", Path.GetDirectoryName(Application.ExecutablePath));
		}

		/// <summary>
		/// The main entry point for the application.
		/// </summary>
		[STAThread]
		static void Main()
		{
			Application.EnableVisualStyles();
			Application.SetCompatibleTextRenderingDefault(false);
			Application.Run(new Form1());
		}
	}
}

Open in new window


Form1.cs -
using System;
using System.Diagnostics;
using System.Windows.Forms;

namespace EE_Q28977165
{
	public partial class Form1 : Form
	{
		public Form1()
		{
			InitializeComponent();
		}

		private void OnLoad(object sender, EventArgs e)
		{
			ProcessStartInfo info = new ProcessStartInfo() { Arguments = "VistaLib32.dll,RunNonElevated cmd.exe", FileName = "RUNDLL32.EXE", UseShellExecute = false };
			Process p = Process.Start(info);
		}
	}
}

Open in new window


NativeMethods.cs -
using System;
using System.Runtime.InteropServices;

namespace EE_Q28977165
{
	static class NativeMethods
	{
		[DllImport("VistaLib32.dll")]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool RunNonElevated(IntPtr hWnd, string pszPath, string pszParameters, string pszDirectory, ref IntPtr phProcess);
	}
}

Open in new window


-saige-
0
 

Author Comment

by:richtelieu88
Comment Utility
Sorry everybody, I got busy with other work related activity.  I need time to build this potential solution from "it_saige".
0
 

Author Comment

by:richtelieu88
Comment Utility
it_saige,

I tried your recommendation:
          "You can either distribute the dll with your application, by adding it to the project and setting the 'Copy to Output Directory' property to either 'Copy always' or 'Copy if newer' -"

But, as before, when I launch VS as ADMIN, then call "RunNonElevated" it does nothing; that is, cmd.exe is not launched.  Nothing happens.

(And, like I mentioned, when I launch VS NOT as admin, cmd.exe does get launched just fine, but it is launched as Administrator.  I can see "Administrator" in the cmd's Title Bar.)

I think there is some other underlying problem here.

Please help, anyone.

Thanks in advance.
0
 
LVL 32

Accepted Solution

by:
it_saige earned 500 total points
Comment Utility
After much trial and error, I came across this implementation and converted it into c#, the same caveats that the original author discusses are applicable.

Form1.cs -
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Text;
using System.Windows.Forms;

// Converted from - https://blogs.msdn.microsoft.com/aaron_margosis/2009/06/06/faq-how-do-i-start-a-program-as-the-desktop-user-from-an-elevated-app/
namespace EE_Q28977165
{
	public partial class Form1 : Form
	{
		public Form1()
		{
			InitializeComponent();
		}

		private void OnLoad(object sender, EventArgs e)
		{
			if (HasAdministrativeRights())
			{
				CreateUnElevatedProcess(@"c:\windows\system32\cmd.exe", string.Empty);
			}
			else
			{
				ProcessStartInfo info = new ProcessStartInfo() { FileName = "cmd.exe", UseShellExecute = false, Verb = "runas" };
				Process p = Process.Start(info);
			}
		}

		bool HasAdministrativeRights()
		{
			return new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);
		}

		void CreateUnElevatedProcess(string fileName, string arguments)
		{
			IntPtr hWnd = IntPtr.Zero;
			uint id = default(uint);
			SafeTokenHandle hShell = default(SafeTokenHandle);
			SafeTokenHandle hShellToken = default(SafeTokenHandle);
			SafeTokenHandle hDupedToken = default(SafeTokenHandle);
			PROCESS_INFORMATION pi = default(PROCESS_INFORMATION);
			STARTUPINFO si = default(STARTUPINFO);
			string path = Path.GetFullPath(fileName);
			string directory = Path.GetDirectoryName(path);

			try
			{
				Privileges.SetPrivilege(SECURITY_ENTITY.SE_INCREASE_QUOTA_NAME, true);
				hWnd = NativeMethods.GetShellWindow();
				if (hWnd != IntPtr.Zero)
				{
					NativeMethods.GetWindowThreadProcessId(hWnd, out id);
					if (id != 0)
					{
						hShell = NativeMethods.OpenProcess(ProcessAccessFlags.QueryInformation, false, (int)id);
						if (hShell != default(SafeTokenHandle) && NativeMethods.OpenProcessToken(hShell, TOKEN_ACCESS_OBJECT.TOKEN_DUPLICATE, out hShellToken))
						{
							var desired = (TOKEN_ACCESS_OBJECT.TOKEN_QUERY | TOKEN_ACCESS_OBJECT.TOKEN_ASSIGN_PRIMARY | 
								TOKEN_ACCESS_OBJECT.TOKEN_DUPLICATE | TOKEN_ACCESS_OBJECT.TOKEN_ADJUST_DEFAULT | TOKEN_ACCESS_OBJECT.TOKEN_ADJUST_SESSIONID);

							if (NativeMethods.DuplicateTokenEx(hShellToken, desired, IntPtr.Zero, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, 
								TOKEN_TYPE.TokenPrimary, out hDupedToken))
							{
								si.cb = Marshal.SizeOf(si);
								si.lpDesktop = string.Empty;
								si.dwFlags = (int)StartFlags.STARTF_USESTDHANDLES;

								if (!NativeMethods.CreateProcessWithTokenW(hDupedToken, LogonFlags.None, path,
									string.Format("\"{0}\" {1}", fileName.Replace("\"", "\"\""), arguments), 0, IntPtr.Zero, directory, ref si, ref pi))
									throw new Win32Exception(Marshal.GetLastWin32Error());
							}
							else
								throw new Win32Exception(Marshal.GetLastWin32Error());
						}
						else
							throw new Win32Exception(Marshal.GetLastWin32Error());
					}
					else
						throw new Win32Exception(Marshal.GetLastWin32Error());
				}
				else
					throw new Win32Exception(Marshal.GetLastWin32Error());
			}
			catch (Exception ex)
			{
				Console.WriteLine("Exception reported - {0} [{1}]", ex.Message, ex);
			}
			finally
			{
				if (hShellToken != default(SafeTokenHandle))
					hShellToken.Close();

				if (hDupedToken != default(SafeTokenHandle))
					hDupedToken.Close();

				if (hShell != default(SafeTokenHandle))
					hShell.Close();

				if (pi.hProcess != IntPtr.Zero)
					NativeMethods.CloseHandle(pi.hProcess);

				if (pi.hThread != IntPtr.Zero)
					NativeMethods.CloseHandle(pi.hThread);
			}
		}
	}
}

Open in new window

Form1.Designer.cs -
namespace EE_Q28977165
{
	partial class Form1
	{
		/// <summary>
		/// Required designer variable.
		/// </summary>
		private System.ComponentModel.IContainer components = null;

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
		protected override void Dispose(bool disposing)
		{
			if (disposing && (components != null))
			{
				components.Dispose();
			}
			base.Dispose(disposing);
		}

		#region Windows Form Designer generated code

		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			this.SuspendLayout();
			// 
			// Form1
			// 
			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
			this.ClientSize = new System.Drawing.Size(284, 261);
			this.Name = "Form1";
			this.Text = "Form1";
			this.Load += new System.EventHandler(this.OnLoad);
			this.ResumeLayout(false);

		}

		#endregion
	}
}

Open in new window

NativeMethods.cs -
using System;
using System.ComponentModel;
using System.Globalization;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;

// Converted from - https://blogs.msdn.microsoft.com/aaron_margosis/2009/06/06/faq-how-do-i-start-a-program-as-the-desktop-user-from-an-elevated-app/
namespace EE_Q28977165
{
	#region Classes
	static class Privileges
	{
		/// <summary>Sets the privilege.</summary>
		/// <param name="se">The se.</param>
		/// <param name="enable">if set to <c>true</c> [enable].</param>
		/// <exception cref="InvalidEnumArgumentException">se</exception>
		/// <exception cref="InvalidOperationException">
		/// AdjustTokenPrivileges failed.
		/// </exception>
		public static void SetPrivilege(SECURITY_ENTITY se, bool enable)
		{
			if (!Enum.IsDefined(typeof(SECURITY_ENTITY), se))
				throw new InvalidEnumArgumentException("se", (int)se, typeof(SECURITY_ENTITY));

			var entity = GetSecurityEntityValue(se);
			try
			{
				var luid = new LUID();

				if (NativeMethods.LookupPrivilegeValue(null, entity, out luid))
				{
					var tp = new TOKEN_PRIVILEGES() { PrivilegeCount = 1, Privileges = new LUID_AND_ATTRIBUTES[1] };
					tp.Privileges[0].Attributes = (uint)(enable ? 2 : 0);
					tp.Privileges[0].Luid = luid;

					var hToken = default(SafeTokenHandle);
					try
					{
						var process = NativeMethods.GetCurrentProcess();
						var previous = default(TOKEN_PRIVILEGES);
						var result = IntPtr.Zero;
						if (NativeMethods.OpenProcessToken(process, TOKEN_ACCESS_OBJECT.TOKEN_ADJUST_PRIVILEGES | TOKEN_ACCESS_OBJECT.TOKEN_QUERY, out hToken))
						{
							if (NativeMethods.AdjustTokenPrivileges(hToken, false, ref tp, (uint)Marshal.SizeOf(tp), ref previous, ref result))
							{
								var lastError = Marshal.GetLastWin32Error();
								if (lastError == NativeMethods.ERROR_NOT_ALL_ASSIGNED)
								{
									var ex = new Win32Exception();
									throw new InvalidOperationException("AdjustTokenPrivileges failed.", ex);
								}
							}
							else
							{
								var ex = new Win32Exception();
								throw new InvalidOperationException("AdjustTokenPrivileges failed.", ex);
							}
						}
						else
						{
							var ex = new Win32Exception();
							var message = string.Format(CultureInfo.InvariantCulture, "OpenProcessToken failed. CurrentProcess: {0}", process.ToInt32());
							throw new InvalidOperationException(message, ex);
						}
					}
					finally
					{
						if (hToken != default(SafeTokenHandle))
							hToken.Close();
					}
				}
				else
				{
					var win32Exception = new Win32Exception();
					var exceptionMessage = string.Format(CultureInfo.InvariantCulture, "LookupPrivilegeValue failed. SecurityEntityValue: {0}", entity);
					throw new InvalidOperationException(exceptionMessage, win32Exception);
				}
			}
			catch (Exception e)
			{
				var exceptionMessage = string.Format(CultureInfo.InvariantCulture, "EnablePrivilege failed. SecurityEntity: {0}", se);
				throw new InvalidOperationException(exceptionMessage, e);
			}
		}

		/// <summary>Gets the security entity value.</summary>
		/// <param name="entity">The security entity.</param>
		/// <returns>System.String.</returns>
		/// <exception cref="ArgumentOutOfRangeException"></exception>
		private static string GetSecurityEntityValue(SECURITY_ENTITY entity)
		{
			switch (entity)
			{
				case SECURITY_ENTITY.SE_ASSIGNPRIMARYTOKEN_NAME:
					return "SeAssignPrimaryTokenPrivilege";
				case SECURITY_ENTITY.SE_AUDIT_NAME:
					return "SeAuditPrivilege";
				case SECURITY_ENTITY.SE_BACKUP_NAME:
					return "SeBackupPrivilege";
				case SECURITY_ENTITY.SE_CHANGE_NOTIFY_NAME:
					return "SeChangeNotifyPrivilege";
				case SECURITY_ENTITY.SE_CREATE_GLOBAL_NAME:
					return "SeCreateGlobalPrivilege";
				case SECURITY_ENTITY.SE_CREATE_PAGEFILE_NAME:
					return "SeCreatePagefilePrivilege";
				case SECURITY_ENTITY.SE_CREATE_PERMANENT_NAME:
					return "SeCreatePermanentPrivilege";
				case SECURITY_ENTITY.SE_CREATE_SYMBOLIC_LINK_NAME:
					return "SeCreateSymbolicLinkPrivilege";
				case SECURITY_ENTITY.SE_CREATE_TOKEN_NAME:
					return "SeCreateTokenPrivilege";
				case SECURITY_ENTITY.SE_DEBUG_NAME:
					return "SeDebugPrivilege";
				case SECURITY_ENTITY.SE_ENABLE_DELEGATION_NAME:
					return "SeEnableDelegationPrivilege";
				case SECURITY_ENTITY.SE_IMPERSONATE_NAME:
					return "SeImpersonatePrivilege";
				case SECURITY_ENTITY.SE_INC_BASE_PRIORITY_NAME:
					return "SeIncreaseBasePriorityPrivilege";
				case SECURITY_ENTITY.SE_INCREASE_QUOTA_NAME:
					return "SeIncreaseQuotaPrivilege";
				case SECURITY_ENTITY.SE_INC_WORKING_SET_NAME:
					return "SeIncreaseWorkingSetPrivilege";
				case SECURITY_ENTITY.SE_LOAD_DRIVER_NAME:
					return "SeLoadDriverPrivilege";
				case SECURITY_ENTITY.SE_LOCK_MEMORY_NAME:
					return "SeLockMemoryPrivilege";
				case SECURITY_ENTITY.SE_MACHINE_ACCOUNT_NAME:
					return "SeMachineAccountPrivilege";
				case SECURITY_ENTITY.SE_MANAGE_VOLUME_NAME:
					return "SeManageVolumePrivilege";
				case SECURITY_ENTITY.SE_PROF_SINGLE_PROCESS_NAME:
					return "SeProfileSingleProcessPrivilege";
				case SECURITY_ENTITY.SE_RELABEL_NAME:
					return "SeRelabelPrivilege";
				case SECURITY_ENTITY.SE_REMOTE_SHUTDOWN_NAME:
					return "SeRemoteShutdownPrivilege";
				case SECURITY_ENTITY.SE_RESTORE_NAME:
					return "SeRestorePrivilege";
				case SECURITY_ENTITY.SE_SECURITY_NAME:
					return "SeSecurityPrivilege";
				case SECURITY_ENTITY.SE_SHUTDOWN_NAME:
					return "SeShutdownPrivilege";
				case SECURITY_ENTITY.SE_SYNC_AGENT_NAME:
					return "SeSyncAgentPrivilege";
				case SECURITY_ENTITY.SE_SYSTEM_ENVIRONMENT_NAME:
					return "SeSystemEnvironmentPrivilege";
				case SECURITY_ENTITY.SE_SYSTEM_PROFILE_NAME:
					return "SeSystemProfilePrivilege";
				case SECURITY_ENTITY.SE_SYSTEMTIME_NAME:
					return "SeSystemtimePrivilege";
				case SECURITY_ENTITY.SE_TAKE_OWNERSHIP_NAME:
					return "SeTakeOwnershipPrivilege";
				case SECURITY_ENTITY.SE_TCB_NAME:
					return "SeTcbPrivilege";
				case SECURITY_ENTITY.SE_TIME_ZONE_NAME:
					return "SeTimeZonePrivilege";
				case SECURITY_ENTITY.SE_TRUSTED_CREDMAN_ACCESS_NAME:
					return "SeTrustedCredManAccessPrivilege";
				case SECURITY_ENTITY.SE_UNDOCK_NAME:
					return "SeUndockPrivilege";
				default:
					throw new ArgumentOutOfRangeException(typeof(SECURITY_ENTITY).Name);
			}
		}
	}

	class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
	{
		private SafeTokenHandle() : base(true) { ;}

		internal SafeTokenHandle(IntPtr handle) : base(true) { base.SetHandle(handle); }

		protected override bool ReleaseHandle()
		{
			return NativeMethods.CloseHandle(base.handle);
		}
	}
	#endregion

	#region Enumerations
	[Flags]
	enum CreationFlags : uint
	{
		DefaultErrorMode = 0x04000000,
		NewConsole = 0x00000010,
		NewProcessGroup = 0x00000200,
		SeparateWOWVDM = 0x00000800,
		Suspended = 0x00000004,
		UnicodeEnvironment = 0x00000400,
		ExtendedStartupInfoPresent = 0x00080000
	}

	enum LogonFlags
	{
		None = 0,
		/// <summary>
		/// Log on, then load the user's profile in the HKEY_USERS registry key. The function
		/// returns after the profile has been loaded. Loading the profile can be time-consuming,
		/// so it is best to use this value only if you must access the information in the 
		/// HKEY_CURRENT_USER registry key. 
		/// NOTE: Windows Server 2003: The profile is unloaded after the new process has been
		/// terminated, regardless of whether it has created child processes.
		/// </summary>
		/// <remarks>See LOGON_WITH_PROFILE</remarks>
		WithProfile = 1,
		/// <summary>
		/// Log on, but use the specified credentials on the network only. The new process uses the
		/// same token as the caller, but the system creates a new logon session within LSA, and
		/// the process uses the specified credentials as the default credentials.
		/// This value can be used to create a process that uses a different set of credentials
		/// locally than it does remotely. This is useful in inter-domain scenarios where there is
		/// no trust relationship.
		/// The system does not validate the specified credentials. Therefore, the process can start,
		/// but it may not have access to network resources.
		/// </summary>
		/// <remarks>See LOGON_NETCREDENTIALS_ONLY</remarks>
		NetCredentialsOnly
	}
	
	[Flags]
	enum ProcessAccessFlags : uint
	{
		All = 0x001F0FFF,
		Terminate = 0x00000001,
		CreateThread = 0x00000002,
		VirtualMemoryOperation = 0x00000008,
		VirtualMemoryRead = 0x00000010,
		VirtualMemoryWrite = 0x00000020,
		DuplicateHandle = 0x00000040,
		CreateProcess = 0x000000080,
		SetQuota = 0x00000100,
		SetInformation = 0x00000200,
		QueryInformation = 0x00000400,
		QueryLimitedInformation = 0x00001000,
		Synchronize = 0x00100000
	}

	enum SECURITY_ENTITY : int
	{
		SE_CREATE_TOKEN_NAME,
		SE_ASSIGNPRIMARYTOKEN_NAME,
		SE_LOCK_MEMORY_NAME,
		SE_INCREASE_QUOTA_NAME,
		SE_UNSOLICITED_INPUT_NAME,
		SE_MACHINE_ACCOUNT_NAME,
		SE_TCB_NAME,
		SE_SECURITY_NAME,
		SE_TAKE_OWNERSHIP_NAME,
		SE_LOAD_DRIVER_NAME,
		SE_SYSTEM_PROFILE_NAME,
		SE_SYSTEMTIME_NAME,
		SE_PROF_SINGLE_PROCESS_NAME,
		SE_INC_BASE_PRIORITY_NAME,
		SE_CREATE_PAGEFILE_NAME,
		SE_CREATE_PERMANENT_NAME,
		SE_BACKUP_NAME,
		SE_RESTORE_NAME,
		SE_SHUTDOWN_NAME,
		SE_DEBUG_NAME,
		SE_AUDIT_NAME,
		SE_SYSTEM_ENVIRONMENT_NAME,
		SE_CHANGE_NOTIFY_NAME,
		SE_REMOTE_SHUTDOWN_NAME,
		SE_UNDOCK_NAME,
		SE_SYNC_AGENT_NAME,
		SE_ENABLE_DELEGATION_NAME,
		SE_MANAGE_VOLUME_NAME,
		SE_IMPERSONATE_NAME,
		SE_CREATE_GLOBAL_NAME,
		SE_CREATE_SYMBOLIC_LINK_NAME,
		SE_INC_WORKING_SET_NAME,
		SE_RELABEL_NAME,
		SE_TIME_ZONE_NAME,
		SE_TRUSTED_CREDMAN_ACCESS_NAME
	}

	enum SECURITY_IMPERSONATION_LEVEL : int
	{
		SecurityAnonymous,
		SecurityIdentification,
		SecurityImpersonation,
		SecurityDelegation
	}

	[Flags]
	enum TOKEN_ACCESS_OBJECT : uint
	{
		TOKEN_ASSIGN_PRIMARY = 0x0001,
		TOKEN_DUPLICATE = 0x0002,
		TOKEN_IMPERSONATE = 0x0004,
		TOKEN_QUERY = 0x0008,
		TOKEN_QUERY_SOURCE = 0x0010,
		TOKEN_ADJUST_PRIVILEGES = 0x0020,
		TOKEN_ADJUST_GROUPS = 0x0040,
		TOKEN_ADJUST_DEFAULT = 0x0080,
		TOKEN_ADJUST_SESSIONID = 0x0100,
		STANDARD_RIGHTS_READ = 0x00020000,
		STANDARD_RIGHTS_REQUIRED = 0x000F0000,
		TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY),
		TOKEN_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_QUERY_SOURCE |
			TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID)
	}

	enum TOKEN_TYPE : int
	{
		TokenPrimary = 1,
		TokenImpersonation
	}

	[Flags]
	enum StartFlags : int
	{
		STARTF_USESHOWWINDOW = 0x00000001,
		STARTF_USESIZE = 0x00000002,
		STARTF_USEPOSITION = 0x00000004,
		STARTF_USECOUNTCHARS = 0x00000008,
		STARTF_USEFILLATTRIBUTE = 0x00000010,
		STARTF_RUNFULLSCREEN = 0x00000020,  // ignored for non-x86 platforms
		STARTF_FORCEONFEEDBACK = 0x00000040,
		STARTF_FORCEOFFFEEDBACK = 0x00000080,
		STARTF_USESTDHANDLES = 0x00000100,
	}
	#endregion

	#region Structures
	[StructLayout(LayoutKind.Sequential)]
	struct LUID
	{
		public uint LowPart;
		public int HighPart;
	}

	[StructLayout(LayoutKind.Sequential)]
	struct LUID_AND_ATTRIBUTES
	{
		public LUID Luid;
		public uint Attributes;
	}

	[StructLayout(LayoutKind.Sequential)]
	struct PROCESS_INFORMATION
	{
		public IntPtr hProcess;
		public IntPtr hThread;
		public int dwProcessId;
		public int dwThreadId;
	}

	[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
	struct STARTUPINFO
	{
		public Int32 cb;
		public string lpReserved;
		public string lpDesktop;
		public string lpTitle;
		public Int32 dwX;
		public Int32 dwY;
		public Int32 dwXSize;
		public Int32 dwYSize;
		public Int32 dwXCountChars;
		public Int32 dwYCountChars;
		public Int32 dwFillAttribute;
		public Int32 dwFlags;
		public Int16 wShowWindow;
		public Int16 cbReserved2;
		public IntPtr lpReserved2;
		public IntPtr hStdInput;
		public IntPtr hStdOutput;
		public IntPtr hStdError;
	}

	[StructLayout(LayoutKind.Sequential)]
	struct TOKEN_PRIVILEGES
	{
		public uint PrivilegeCount;
		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1, ArraySubType = UnmanagedType.Struct)]
		public LUID_AND_ATTRIBUTES[] Privileges;
	}
	#endregion

	#region Native Methods
	static class NativeMethods
	{
		public const int ERROR_NOT_ALL_ASSIGNED = 1300;

		[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool AdjustTokenPrivileges(SafeTokenHandle TokenHandle, [MarshalAs(UnmanagedType.Bool)] bool DisableAllPrivileges,
			[MarshalAs(UnmanagedType.Struct)]ref TOKEN_PRIVILEGES Newstate, uint BufferLength, 
			[MarshalAs(UnmanagedType.Struct)]ref TOKEN_PRIVILEGES PreviousState, ref IntPtr ReturnLength);

		[DllImport("kernel32.dll", SetLastError = true)]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool CloseHandle(IntPtr hObject);

		[DllImport("advapi32", SetLastError = true, CharSet = CharSet.Unicode)]
		public static extern bool CreateProcessWithTokenW(SafeTokenHandle hToken, LogonFlags dwLogonFlags, string lpApplicationName, string lpCommandLine, 
			CreationFlags dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFO lpStartupInfo, 
			ref PROCESS_INFORMATION lpProcessInformation);
		
		[DllImport("advapi32", CharSet = CharSet.Auto, SetLastError = true)]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool DuplicateTokenEx(SafeTokenHandle hExistingToken, TOKEN_ACCESS_OBJECT desiredAccess, IntPtr pTokenAttributes,
			SECURITY_IMPERSONATION_LEVEL ImpersonationLevel, TOKEN_TYPE TokenType, out SafeTokenHandle hNewToken);

		[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
		public static extern IntPtr GetCurrentProcess();

		[DllImport("user32.dll", SetLastError = true)]
		public static extern IntPtr GetShellWindow();

		[DllImport("user32.dll", SetLastError = true)]
		public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

		[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, out LUID lpLuid);

		[DllImport("kernel32.dll", SetLastError = true)]
		public static extern SafeTokenHandle OpenProcess(ProcessAccessFlags processAccess, bool bInheritHandle, int processId);

		[DllImport("advapi32", CharSet = CharSet.Auto, SetLastError = true)]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool OpenProcessToken(IntPtr hProcess, TOKEN_ACCESS_OBJECT desiredAccess, out SafeTokenHandle hToken);

		[DllImport("advapi32", CharSet = CharSet.Auto, SetLastError = true)]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool OpenProcessToken(SafeTokenHandle hProcess, TOKEN_ACCESS_OBJECT desiredAccess, out SafeTokenHandle hToken);
	}
	#endregion
}

Open in new window


-saige-
0
 

Author Comment

by:richtelieu88
Comment Utility
it_saige,

Damn, I hope your code works.  I will try to find time to test it tomorrow.

Thanks
0
 

Author Comment

by:richtelieu88
Comment Utility
Good work it_saige !
0
 

Author Closing Comment

by:richtelieu88
Comment Utility
Good job
0
 
LVL 32

Expert Comment

by:it_saige
Comment Utility
Glad to be of assistance

-saige-
0

Featured Post

Why You Should Analyze Threat Actor TTPs

After years of analyzing threat actor behavior, it’s become clear that at any given time there are specific tactics, techniques, and procedures (TTPs) that are particularly prevalent. By analyzing and understanding these TTPs, you can dramatically enhance your security program.

Join & Write a Comment

This is an article about Leadership and accepting and adapting to new challenges. It focuses mostly on upgrading to Windows 10.
My experience with Windows 10 over a one year period and suggestions for smooth operation
Windows 8 comes with a dramatically different user interface known as Metro. Notably missing from the new interface is a Start button and Start Menu. Many users do not like it, much preferring the interface of earlier versions — Windows 7, Windows X…
Windows 8 came with a dramatically different user interface known as Metro. Notably missing from that interface was a Start button and Start Menu. Microsoft responded to negative user feedback of the Metro interface, bringing back the Start button a…

762 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now