CreateProcessAsUser with command line arguments - c#

I am trying to start a process with CreateProcessAsUser and works fine BUT in case a command line arguments is present the function fails and the last error is set to ERROR_UNKNOWN_REVISION.
I have tried multiple combinations for command line with no luck.
Here is the code
var CMD_ARGS = Environment.GetCommandLineArgs();
string FILE_NAME = EntryPoint.PRCOESS_FILE_NAME;
string WORKING_DIRECTORY = EntryPoint.PROCESS_DIRECTORY;
string ARGUMENTS = CMD_ARGS.Skip(1).Aggregate((first,next)=> ' ' + first + ' ' + next);
IntPtr USER_TOKEN = IntPtr.Zero;
IntPtr ENVIRONMENT=IntPtr.Zero;
try
{
var consoleSession = Kernel32.WTSGetActiveConsoleSessionId();
if (consoleSession == 0xFFFFFFFF)
return;
if (!Wtsapi32.WTSQueryUserToken(consoleSession, out USER_TOKEN))
throw new Win32Exception();
if (!Userenv.CreateEnvironmentBlock(out ENVIRONMENT, USER_TOKEN, true))
throw new Win32Exception();
//initialize structures
SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
sa.nLength = Marshal.SizeOf(sa);
//initialize startup
STARTUPINFO si = new STARTUPINFO();
si.cb = Marshal.SizeOf(si);
//initialize process info
var PROCESS_INFO = new PROCESS_INFORMATION();
int dwCreationFlags = NORMAL_PRIORITY_CLASS | (int)(PROCESS_CREATE_FLAG.CREATE_NEW_CONSOLE | PROCESS_CREATE_FLAG.CREATE_UNICODE_ENVIRONMENT);
if (!AdvApi32.CreateProcessAsUser(USER_TOKEN,
FILE_NAME,
ARGUMENTS,
ref sa,
ref sa,
true,
dwCreationFlags,
ENVIRONMENT,
WORKING_DIRECTORY,
ref si,
out PROCESS_INFO))
throw new Win32Exception();
if (PROCESS_INFO.hThread != IntPtr.Zero)
{
ClientProcessId = PROCESS_INFO.dwProcessId;
ClientSessionId = consoleSession;
Trace.WriteLine($"{PROCESS_INFO.dwProcessId}");
}
}
catch(Exception ex)
{
throw;
}
finally
{
Userenv.DestroyEnvironmentBlock(ENVIRONMENT);
Kernel32.CloseHandle(USER_TOKEN);
}

Related

How to use windows service to start a software as an administrator?

My program refers to Cjwdev.windowsApi to start the software, but now there is a software that must be run as an administrator, how can I do it?
The following is part of the code:
string appStartPath = #"D:\Program Files (x86)\tencent\QQ\Bin\QQScLauncher.exe";
IntPtr userTokenHandle = IntPtr.Zero;
ApiDefinitions.WTSQueryUserToken(ApiDefinitions.WTSGetActiveConsoleSessionId(), ref userTokenHandle);
ApiDefinitions.PROCESS_INFORMATION procInfo = new ApiDefinitions.PROCESS_INFORMATION();
ApiDefinitions.STARTUPINFO startInfo = new ApiDefinitions.STARTUPINFO();
startInfo.cb = (uint)System.Runtime.InteropServices.Marshal.SizeOf(startInfo);
// begin start
ApiDefinitions.CreateProcessAsUser(
userTokenHandle,
appStartPath,
"",
IntPtr.Zero,
IntPtr.Zero,
false,
0,
IntPtr.Zero,
null,
ref startInfo,
out procInfo);
if (userTokenHandle != IntPtr.Zero)
ApiDefinitions.CloseHandle(userTokenHandle);
int _currentAquariusProcessId = (int)procInfo.dwProcessId;

How to get a active file/Application Complete path with file Extension

i am trying to track active Application/File on my system using Windows application(c#).
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
private string GetActiveWindowTitle()
{
const int nChars = 256;
StringBuilder Buff = new StringBuilder(nChars);
IntPtr handle = GetForegroundWindow();
if (GetWindowText(handle, Buff, nChars) > 0)
{
return Buff.ToString() + " " + handle;
}
return null;
}
private string GetActiveWindowPath()
{
const int nChars = 256;
StringBuilder Buff = new StringBuilder(nChars);
IntPtr handle = GetForegroundWindow();
int handleint = int.Parse(handle + "");
SHDocVw.ShellWindows explorer = new SHDocVw.ShellWindows();
//var xy = new SHDocVw.InternetExplorerMedium();
var xpl = explorer.Cast<SHDocVw.InternetExplorerMedium>().Where(hwnd => hwnd.HWND == handleint).FirstOrDefault();
if (xpl != null)
{
string path = new Uri(xpl.LocationURL).LocalPath;
return ("location:" + xpl.LocationName + " path:" + path);
}
return "HWND" + handleint;
}
But by using the above code i only getting file title not the complete file name with extension and by using other method i am just getting folder info.
But i am trying to get the file extension with path
Eg: D:\New Folder\sampleFile.txt
public static string GetMainModuleFilepath(int processId)
{
string wmiQueryString = "SELECT * FROM Win32_Process WHERE ProcessId = " + processId;
using (var searcher = new ManagementObjectSearcher(wmiQueryString))
{
using (var results = searcher.Get())
{
ManagementObject mo = results.Cast<ManagementObject>().FirstOrDefault();
if (mo != null)
{
return (string)mo["CommandLine"];
}
}
}
Process testProcess = Process.GetProcessById(processId);
return null;
}
by using this function you will get a string like
"c:..\notepade.exe" D:\New Folder\sampleFile.txt"
after that split as you like to get the path.
I have written this code for office 2007 you guys can debug and find the proper path for the higher versions.

Running a Process (WITH GUI) on Windows XP Logon Screen (.NET / Pinvoke)

I need to write a small service that runs an application (with gui, e.g. calc.exe) on the logon-screen.
I already found this question (and answer):
Running a process at the Windows 7 Welcome Screen
please read the code-comments if you don't understand how this works:
// grab the winlogon process
Process winLogon = null;
foreach (Process p in Process.GetProcesses())
{
if (p.ProcessName.Contains("winlogon"))
{
winLogon = p;
break;
}
}
// grab the winlogon's token
IntPtr userToken = IntPtr.Zero;
if (!OpenProcessToken(winLogon.Handle, TOKEN_QUERY | TOKEN_IMPERSONATE | TOKEN_DUPLICATE, out userToken))
{
log("ERROR: OpenProcessToken returned false - " + Marshal.GetLastWin32Error());
}
// create a new token
IntPtr newToken = IntPtr.Zero;
SECURITY_ATTRIBUTES tokenAttributes = new SECURITY_ATTRIBUTES();
tokenAttributes.nLength = Marshal.SizeOf(tokenAttributes);
SECURITY_ATTRIBUTES threadAttributes = new SECURITY_ATTRIBUTES();
threadAttributes.nLength = Marshal.SizeOf(threadAttributes);
// duplicate the winlogon token to the new token
if (!DuplicateTokenEx(userToken, 0x10000000, ref tokenAttributes, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation,
TOKEN_TYPE.TokenImpersonation, out newToken))
{
log("ERROR: DuplicateTokenEx returned false - " + Marshal.GetLastWin32Error());
}
TOKEN_PRIVILEGES tokPrivs = new TOKEN_PRIVILEGES();
tokPrivs.PrivilegeCount = 1;
LUID seDebugNameValue = new LUID();
if (!LookupPrivilegeValue(null, SE_DEBUG_NAME, out seDebugNameValue))
{
log("ERROR: LookupPrivilegeValue returned false - " + Marshal.GetLastWin32Error());
}
tokPrivs.Privileges = new LUID_AND_ATTRIBUTES[1];
tokPrivs.Privileges[0].Luid = seDebugNameValue;
tokPrivs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
// escalate the new token's privileges
if (!AdjustTokenPrivileges(newToken, false, ref tokPrivs, 0, IntPtr.Zero, IntPtr.Zero))
{
log("ERROR: AdjustTokenPrivileges returned false - " + Marshal.GetLastWin32Error());
}
PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
STARTUPINFO si = new STARTUPINFO();
si.cb = Marshal.SizeOf(si);
si.lpDesktop = "Winsta0\\Winlogon";
// start the process using the new token
if (!CreateProcessAsUser(newToken, "calc.exe", null, ref tokenAttributes, ref threadAttributes,
true, (uint)CreateProcessFlags.CREATE_NEW_CONSOLE | (uint)CreateProcessFlags.INHERIT_CALLER_PRIORITY, IntPtr.Zero,
"C:\\Windows\\System32", ref si, out pi))
{
log("ERROR: CreateProcessAsUser returned false - " + Marshal.GetLastWin32Error());
}
Process _p = Process.GetProcessById(pi.dwProcessId);
if (_p != null)
{
log("Process " + _p.Id + " Name " + _p.ProcessName);
}
else
{
log("Process not found");
}
It works with Windows 7. With XP I get error 1349 ERROR_BAD_TOKEN_TYPE while calling CreateProcessAsUser (MSDN: The type of the token is inappropriate for its attempted use.).
How to realize this in Windows XP? It does not have to be the code from above but it should work as a service (with system-account?).
Thanks for your support
Fluxer
This definitely has to do with privilege issues (Windows Vista and 7 have notable changes in security). Rather than trying to get token of winlogon.exe and impersonating it, try getting user token via WTSQueryUserToken like this:
WTSQueryUserToken (WTSGetActiveConsoleSessionId(), out userToken);
Replace the OpenProcessToken line which is used to get the token with the above statement.
Your new code should be like this:
// if (!OpenProcessToken(winLogon.Handle, TOKEN_QUERY | TOKEN_IMPERSONATE | //TOKEN_DUPLICATE, out userToken))
// {
// log("ERROR: OpenProcessToken returned false - " + //Marshal.GetLastWin32Error());
// }
WTSQueryUserToken (WTSGetActiveConsoleSessionId(), out userToken);
Do your dll import like this:
[DllImport("Kernel32.dll", SetLastError = true)]
[return:MarshalAs(UnmanagedType.U4)]
public static extern int WTSGetActiveConsoleSessionId ( );
You need only replace the part I commented out with this code.

Running a Form in Windows Logon Screen C#

I need to write a small tool that runs on every userdesktop or, if no one is logged in, directly on logon screen. Maybe a service with a form starting?
I already found this question (and answer):
Running a process at the Windows 7 Welcome Screen
// grab the winlogon process
Process winLogon = null;
foreach (Process p in Process.GetProcesses()) {
if (p.ProcessName.Contains("winlogon")) {
winLogon = p;
break;
}
}
// grab the winlogon's token
IntPtr userToken = IntPtr.Zero;
if (!OpenProcessToken(winLogon.Handle, TOKEN_QUERY | TOKEN_IMPERSONATE | TOKEN_DUPLICATE, out userToken)) {
log("ERROR: OpenProcessToken returned false - " + Marshal.GetLastWin32Error());
}
// create a new token
IntPtr newToken = IntPtr.Zero;
SECURITY_ATTRIBUTES tokenAttributes = new SECURITY_ATTRIBUTES();
tokenAttributes.nLength = Marshal.SizeOf(tokenAttributes);
SECURITY_ATTRIBUTES threadAttributes = new SECURITY_ATTRIBUTES();
threadAttributes.nLength = Marshal.SizeOf(threadAttributes);
// duplicate the winlogon token to the new token
if (!DuplicateTokenEx(userToken, 0x10000000, ref tokenAttributes, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation,
TOKEN_TYPE.TokenImpersonation, out newToken)) {
log("ERROR: DuplicateTokenEx returned false - " + Marshal.GetLastWin32Error());
}
TOKEN_PRIVILEGES tokPrivs = new TOKEN_PRIVILEGES();
tokPrivs.PrivilegeCount = 1;
LUID seDebugNameValue = new LUID();
if (!LookupPrivilegeValue(null, SE_DEBUG_NAME, out seDebugNameValue)) {
log("ERROR: LookupPrivilegeValue returned false - " + Marshal.GetLastWin32Error());
}
tokPrivs.Privileges = new LUID_AND_ATTRIBUTES[1];
tokPrivs.Privileges[0].Luid = seDebugNameValue;
tokPrivs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
// escalate the new token's privileges
if (!AdjustTokenPrivileges(newToken, false, ref tokPrivs, 0, IntPtr.Zero, IntPtr.Zero)) {
log("ERROR: AdjustTokenPrivileges returned false - " + Marshal.GetLastWin32Error());
}
PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
STARTUPINFO si = new STARTUPINFO();
si.cb = Marshal.SizeOf(si);
si.lpDesktop = "Winsta0\\Winlogon";
// start the process using the new token
if (!CreateProcessAsUser(newToken, process, process, ref tokenAttributes, ref threadAttributes,
true, (uint)CreateProcessFlags.CREATE_NEW_CONSOLE | (uint)CreateProcessFlags.INHERIT_CALLER_PRIORITY, IntPtr.Zero,
logInfoDir, ref si, out pi)) {
log("ERROR: CreateProcessAsUser returned false - " + Marshal.GetLastWin32Error());
}
Process _p = Process.GetProcessById(pi.dwProcessId);
if (_p != null) {
log("Process " + _p.Id + " Name " + _p.ProcessName);
} else {
log("Process not found");
}
But there are no dll-imports explained, so i can't build this.
Thanks for your effort
Fluxer

How to replicate the file/directory permissions with P/Invoke?

I'm searching for the solution for hours, but the documentation being sparse, I'm unable to find what I need.
If I have a file or a directory, how can I, in C# with P/Invoke, duplicate the permissions (ie. the owner and SIDs with their permissions) from one file to another or from one directory to another, assuming that those files and directories are either on the same machine or the network controlled by the same Active Directory server?
Here's an example using GetNamedSecurityInfo and
SetNamedSecurityInfo.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security.AccessControl;
namespace PermissionsExample
{
class Program
{
static void Main(string[] args)
{
string source = "C:\\source.txt";
string dest = "C:\\dest.txt";
string result = CopyPermissions(source, dest);
if (!string.IsNullOrEmpty(result)) { Console.WriteLine(result); }
else { Console.WriteLine("SUCCESS"); }
}
public static string CopyPermissions(string source_file, string dest_file)
{
string errmsg = string.Empty;
IntPtr sidOwner = IntPtr.Zero;
IntPtr sidOwnerDescriptor = IntPtr.Zero;
IntPtr sidGroup = IntPtr.Zero;
IntPtr sidGroupDescriptor = IntPtr.Zero;
IntPtr dacl = IntPtr.Zero;
IntPtr daclDescriptor = IntPtr.Zero;
IntPtr sacl = IntPtr.Zero;
try
{
int result = GetNamedSecurityInfo(source_file, SE_OBJECT_TYPE.SE_FILE_OBJECT, SecurityInfos.DiscretionaryAcl, out sidOwner, out sidGroup, out dacl, out sacl, out daclDescriptor);
if (result != 0)
{
Win32Exception e = new Win32Exception(result);
errmsg = "ERROR: " + e.Message;
return errmsg;
}
result = GetNamedSecurityInfo(source_file, SE_OBJECT_TYPE.SE_FILE_OBJECT, SecurityInfos.Owner, out sidOwner, out sidGroup, out dacl, out sacl, out sidGroupDescriptor);
if (result != 0)
{
Win32Exception e = new Win32Exception(result);
errmsg = "ERROR: " + e.Message;
return errmsg;
}
result = GetNamedSecurityInfo(source_file, SE_OBJECT_TYPE.SE_FILE_OBJECT, SecurityInfos.Group, out sidOwner, out sidGroup, out dacl, out sacl, out sidGroupDescriptor);
if (result != 0)
{
Win32Exception e = new Win32Exception(result);
errmsg = "ERROR: " + e.Message;
return errmsg;
}
SecurityInfos info = SecurityInfos.DiscretionaryAcl | SecurityInfos.Group | SecurityInfos.Owner;
result = SetNamedSecurityInfo(dest_file, SE_OBJECT_TYPE.SE_FILE_OBJECT, info, sidOwner, sidGroup, dacl, sacl);
if (result != 0)
{
Win32Exception e = new Win32Exception(result);
errmsg = "ERROR: " + e.Message;
return errmsg;
}
}
finally
{
if (sidOwnerDescriptor != IntPtr.Zero && LocalFree(sidOwnerDescriptor) != IntPtr.Zero)
{
int err = Marshal.GetLastWin32Error();
Win32Exception e = new Win32Exception(err);
errmsg += "ERROR: " + e.Message;
}
if (sidGroupDescriptor != IntPtr.Zero && LocalFree(sidGroupDescriptor) != IntPtr.Zero)
{
int err = Marshal.GetLastWin32Error();
Win32Exception e = new Win32Exception(err);
errmsg += "ERROR: " + e.Message;
}
if (daclDescriptor != IntPtr.Zero && LocalFree(daclDescriptor) != IntPtr.Zero)
{
int err = Marshal.GetLastWin32Error();
Win32Exception e = new Win32Exception(err);
errmsg += "ERROR: " + e.Message;
}
}
return errmsg;
}
public enum SE_OBJECT_TYPE
{
SE_UNKNOWN_OBJECT_TYPE = 0,
SE_FILE_OBJECT,
SE_SERVICE,
SE_PRINTER,
SE_REGISTRY_KEY,
SE_LMSHARE,
SE_KERNEL_OBJECT,
SE_WINDOW_OBJECT,
SE_DS_OBJECT,
SE_DS_OBJECT_ALL,
SE_PROVIDER_DEFINED_OBJECT,
SE_WMIGUID_OBJECT,
SE_REGISTRY_WOW64_32KEY
}
[DllImport("advapi32.dll", EntryPoint = "GetNamedSecurityInfoW", ExactSpelling = true, CharSet = CharSet.Unicode)]
private static extern int GetNamedSecurityInfo(string objectName, SE_OBJECT_TYPE objectType,
System.Security.AccessControl.SecurityInfos securityInfo, out IntPtr sidOwner,
out IntPtr sidGroup, out IntPtr dacl, out IntPtr sacl, out IntPtr securityDescriptor);
[DllImport("advapi32.dll", EntryPoint = "SetNamedSecurityInfoW", ExactSpelling = true, CharSet = CharSet.Unicode)]
private static extern int SetNamedSecurityInfo(string objectName, SE_OBJECT_TYPE objectType,
System.Security.AccessControl.SecurityInfos securityInfo, IntPtr sidOwner,
IntPtr sidGroup, IntPtr dacl, IntPtr sacl);
[DllImport("kernel32.dll", EntryPoint = "LocalFree", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Unicode)]
private static extern IntPtr LocalFree(IntPtr hMem);
}
}

Categories