Службе Windows не удается запустить интерактивный процесс при входе пользователя в систему с помощью WTSQueryUserToken для некоторых версий Windows
Перейдя по этой ссылке, я реализовал WTSQueryUserToken в своем решении C# и назвал CreateProcessAsUserWrapper.LaunchChildProcess("app_path")
метод из OnStart
моей службы Windows, которая работает как "LocalSystem". Он может запустить процесс, который может взаимодействовать с рабочим столом, но для Windows Professional, а не для Windows Ultimate. Я попробовал это на 64-битной Windows Professional, и он может успешно запустить процесс при входе пользователя в систему в интерактивном режиме, но на 64-битной версии Windows Ultimate CreateProcessAsUser
метод возвращает false, и служба может быть запущена на SCM
в то время как процесс не.
Мой процесс представляет собой приложение формы win, которое должно работать с текущей зарегистрированной учетной записью пользователя для взаимодействия с рабочим столом и использует InteropServices
, Может ли это быть рамочной проблемой? Как добраться до проблемы? Пожалуйста помоги.
Изменить: обе машины работают под управлением Windows 7 64-разрядной. Я добавил следующий код, чтобы получить код ошибки после CreateProcessAsUser
и возвращает "Запрошенная операция требует повышения прав" как ошибка.
string errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
Это происходит только в Windows 7 Ultimate, а не в Windows 7 Professional. Мне нужно запустить свой процесс для каждого вошедшего в систему пользователя, и поэтому я думаю, что не могу использовать CreateProcessWithLogonW
метод вместо. Как я могу исправить эту проблему с правами на повышение прав сейчас?
Изменить: было 2 app.manifest
файлы, один внутри сервисного проекта, а другой внутри проекта win form (process). Я удалил оба файла, построил msi
с WiX
и проверил снова. Теперь он успешно работает на каждой машине, процесс запускается с каждой зарегистрированной учетной записью пользователя. Но одна проблема возникает сейчас. На самом деле мне нужно написать какой-нибудь журнал в папке windows / system, чего я сейчас не могу сделать. Я получаю " UnauthorizedAccessException
"выброшено из процесса. Более того, ни одному пользователю, кроме администратора, не разрешено останавливать или удалять процесс, но в этом случае пользователь может запустить диспетчер задач и завершить процесс оттуда, поскольку он работает с соответствующей учетной записью пользователя. Можно ли запустить процесс с учетной записью пользователя, но с повышенными привилегиями, чтобы я мог записать свой журнал в папку windows / system и не дать пользователю остановить процесс без разрешения администратора? По крайней мере, решить первую проблему, т.е. записать файлы журнала под систему папка очень важна для меня.
Изменить: после добавления SetTokenInformation
вместе с DuplicateTokenEx
мой сервис не запускает приложение с журналом SetTokenInformation()
возвращает ЛОЖЬ. ОШИБКА: требуемая привилегия не удерживается клиентом ". Я предоставляю полный код, пожалуйста, проверьте, правильно ли я обновил его на всех этапах.
using System;
using System.IO;
using System.ComponentModel;
using System.Security.Principal;
using System.Text;
using System.Collections;
using System.Web;
using System.Net;
using System.Diagnostics;
using System.Runtime.InteropServices; // DllImport
namespace CSCreateProcessAsUserFromService
{
class CreateProcessAsUserWrapper1
{
static String tmp_log = Path.Combine(CreateDirectoryAtSystemFolder(), "Temp-Report-Service.txt");
static string errorMessage;
public static void LaunchChildProcess(string ChildProcName)
{
IntPtr ppSessionInfo = IntPtr.Zero;
UInt32 SessionCount = 0;
File.AppendAllText(tmp_log, "\n" + "LaunchChildProcess() OK 1" + "\n");
if (WTSEnumerateSessions(
(IntPtr)WTS_CURRENT_SERVER_HANDLE, // Current RD Session Host Server handle would be zero.
0, // This reserved parameter must be zero.
1, // The version of the enumeration request must be 1.
ref ppSessionInfo, // This would point to an array of session info.
ref SessionCount // This would indicate the length of the above array.
))
{
File.AppendAllText(tmp_log, "\n" + "WTSEnumerateSessions OK 2" + "\n");
for (int nCount = 0; nCount < SessionCount; nCount++)
{
// Extract each session info and check if it is the
// "Active Session" of the current logged-on user.
//ppSessionInfo = new IntPtr(ppSessionInfo.ToInt32() + nCount * Marshal.SizeOf(typeof(WTS_SESSION_INFO)));
WTS_SESSION_INFO tSessionInfo = (WTS_SESSION_INFO)Marshal.PtrToStructure(
ppSessionInfo + nCount * Marshal.SizeOf(typeof(WTS_SESSION_INFO)),
typeof(WTS_SESSION_INFO)
);
/*WTS_SESSION_INFO tSessionInfo = (WTS_SESSION_INFO)Marshal.PtrToStructure(
new IntPtr(ppSessionInfo.ToInt32() + nCount * Marshal.SizeOf(typeof(WTS_SESSION_INFO))),
typeof(WTS_SESSION_INFO)
);*/
if (WTS_CONNECTSTATE_CLASS.WTSActive == tSessionInfo.State)
{
WindowsIdentity m_ImpersonatedUser;
IntPtr hToken = IntPtr.Zero;
IntPtr hTokenDuplicate = IntPtr.Zero;
const int SecurityImpersonation = 2;
const int TokenType = 1;
File.AppendAllText(tmp_log, "\n" + "if (WTS_CONNECTSTATE_CLASS.WTSActive == tSessionInfo.State) 3" + "\n");
if (RevertToSelf())
{
File.AppendAllText(tmp_log, "\n" + "RevertToSelf() TRUE 4 " + "\n");
if (WTSQueryUserToken(tSessionInfo.SessionID, out hToken))
{
File.AppendAllText(tmp_log, "\n" + "if (WTSQueryUserToken(tSessionInfo.SessionID, out hToken)) TRUE 5 " + "\n");
LUID luid = new LUID();
// Security attibute structure used in DuplicateTokenEx and CreateProcessAsUser
// I would prefer to not have to use a security attribute variable and to just
// simply pass null and inherit (by default) the security attributes
// of the existing token. However, in C# structures are value types and therefore
// cannot be assigned the null value.
SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
sa.Length = Marshal.SizeOf(sa);
//if (DuplicateToken(hToken, SecurityImpersonation, ref hTokenDuplicate) != 0)
if (DuplicateTokenEx(hToken, MAXIMUM_ALLOWED/*GENERIC_ACCESS*/, ref sa,
(int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,
(int)TOKEN_TYPE.TokenPrimary, out/*ref*/ hTokenDuplicate))
{
File.AppendAllText(tmp_log, "\n" + " DuplicateTokenEx() TRUE 6 " + "\n");
WindowsImpersonationContext m_ImpersonationContext;
m_ImpersonatedUser = new WindowsIdentity(hTokenDuplicate);
using (m_ImpersonationContext = m_ImpersonatedUser.Impersonate())
{
if (m_ImpersonationContext != null)
{
File.AppendAllText(tmp_log, Environment.NewLine + "User Name: " +
WindowsIdentity.GetCurrent(TokenAccessLevels.MaximumAllowed).Name +
Environment.NewLine + "SID: " +
WindowsIdentity.GetCurrent(TokenAccessLevels.MaximumAllowed).User.Value);
TOKEN_PRIVILEGES tp = new TOKEN_PRIVILEGES
{
PrivilegeCount = 1,
Privileges = new Int32[3]
};
tp.Privileges[1] = luid.HighPart;
tp.Privileges[0] = luid.LowPart;
tp.Privileges[2] = SE_PRIVILEGE_ENABLED;
//Adjust Token privilege
if (SetTokenInformation(hTokenDuplicate,
TOKEN_INFORMATION_CLASS.TokenSessionId,
ref tSessionInfo.SessionID,
(UInt32)Marshal.SizeOf(tSessionInfo.SessionID)))
{
File.AppendAllText(tmp_log, "\n" + " SetTokenInformation() TRUE 7 " + "\n");
if (AdjustTokenPrivileges(hTokenDuplicate,
false, ref tp, Marshal.SizeOf(tp),
IntPtr.Zero, IntPtr.Zero))
{
File.AppendAllText(tmp_log, "\n" + " AdjustTokenPrivileges() TRUE 8 " + "\n");
//ImpersonateLoggedOnUser(hToken);
//WindowsIdentity.Impersonate(hToken);
//errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
//File.AppendAllText(tmp_log, "\n" + "LaunchChildProcess-ImpersonateLoggedOnUser-" + errorMessage + "\n");
// Launch the child process interactively
// with the token of the logged-on user.
PROCESS_INFORMATION tProcessInfo;
STARTUPINFO tStartUpInfo = new STARTUPINFO();
tStartUpInfo.cb = Marshal.SizeOf(typeof(STARTUPINFO));
tStartUpInfo.lpDesktop = @"winsta0\default"; // interactive window station parameter; basically this indicates that the process created can display a GUI on the desktop
// flags that specify the priority and creation method of the process
int dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;
IntPtr pEnv = IntPtr.Zero;
if (CreateEnvironmentBlock(ref pEnv, hTokenDuplicate, true))
{
dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
File.AppendAllText(tmp_log, "\n" + " CreateEnvironmentBlock() TRUE 9 " + "\n");
}
else
{
pEnv = IntPtr.Zero;
File.AppendAllText(tmp_log, "\n" + " CreateEnvironmentBlock() ELSE 9 " + "\n");
}
// create a new process in the current user's logon session
bool ChildProcStarted = CreateProcessAsUser(
hTokenDuplicate, // client's access token
ChildProcName, // file to execute
null, // command line
ref sa, // pointer to process SECURITY_ATTRIBUTES
ref sa, // pointer to thread SECURITY_ATTRIBUTES
false, // handles are not inheritable
dwCreationFlags, // creation flags
IntPtr.Zero, // pointer to new environment block
null, // name of current directory
ref tStartUpInfo, // pointer to STARTUPINFO structure
out tProcessInfo // receives information about new process
);
/*bool ChildProcStarted = CreateProcessAsUser(
//hToken, // Token of the logged-on user.
hTokenDuplicate, // Token of the logged-on user.
ChildProcName, // Name of the process to be started.
null, // Any command line arguments to be passed.
IntPtr.Zero, // Default Process' attributes.
IntPtr.Zero, // Default Thread's attributes.
false, // Does NOT inherit parent's handles.
0, // No any specific creation flag.
null, // Default environment path.
null, // Default current directory.
ref tStartUpInfo, // Process Startup Info.
out tProcessInfo // Process information to be returned.
);*/
errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
File.AppendAllText(tmp_log, "\n" + "LaunchChildProcess-CreateProcessAsUser-" + errorMessage + "\n");
if (ChildProcStarted)
{
// The child process creation is successful!
// If the child process is created, it can be controlled via the out
// param "tProcessInfo". For now, as we don't want to do any thing
// with the child process, closing the child process' handles
// to prevent the handle leak.
CloseHandle(tProcessInfo.hThread);
CloseHandle(tProcessInfo.hProcess);
File.AppendAllText(tmp_log, "\n" + "ChildProcStarted() 10 true!" + "\n");
}
else
{
// CreateProcessAsUser failed!
errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
File.AppendAllText(tmp_log, "\n" + " ChildProcStarted() 10 failed! ERROR: " + errorMessage + "\n");
}
// Whether child process was created or not, close the token handle
// and break the loop as processing for current active user has been done.
CloseHandle(hToken);
CloseHandle(hTokenDuplicate);
// Undo impersonation
//m_ImpersonationContext.Undo();
break;
}
else
{
errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
File.AppendAllText(tmp_log, "\n" + " AdjustTokenPrivileges() FALSE 8 ERROR: " + errorMessage + "\n");
}
}
else
{
errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
//File.AppendAllText(tmp_log, "\n" + "LaunchChildProcess-ImpersonateLoggedOnUser-" + errorMessage + "\n");
File.AppendAllText(tmp_log, "\n" + " SetTokenInformation() FALSE 7 ERROR: " + errorMessage + "\n");
}
}
}
}
else
{
errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
File.AppendAllText(tmp_log, "\n" + "DuplicateTokenEx() FALSE 6 ERROR: " + errorMessage + "\n");
}
}
else
{
// WTSQueryUserToken failed!
errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
File.AppendAllText(tmp_log, "\n" + "if (WTSQueryUserToken(tSessionInfo.SessionID, out hToken)) FALSE 5 ERROR: " + errorMessage + "\n");
}
}
else
{
errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
File.AppendAllText(tmp_log, "\n" + "RevertToSelf() FALSE 4 ERROR: " + errorMessage + "\n");
}
}
else
{
// This Session is not active!
errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
File.AppendAllText(tmp_log, "\n" + "if (WTS_CONNECTSTATE_CLASS.WTSActive == tSessionInfo.State) FALSE 3 This Session is not active! ERROR: " + errorMessage + "\n");
}
}
// Free the memory allocated for the session info array.
WTSFreeMemory(ppSessionInfo);
}
else
{
// WTSEnumerateSessions failed!
errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
File.AppendAllText(tmp_log, "\n" + "WTSEnumerateSessions FAILED 2 ERROR: " + errorMessage + "\n");
}
}
#region P/Invoke WTS APIs
/// <summary>
/// Struct, Enum and P/Invoke Declarations of WTS APIs.
/// </summary>
///
private const int WTS_CURRENT_SERVER_HANDLE = 0;
private enum WTS_CONNECTSTATE_CLASS
{
WTSActive,
WTSConnected,
WTSConnectQuery,
WTSShadow,
WTSDisconnected,
WTSIdle,
WTSListen,
WTSReset,
WTSDown,
WTSInit
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private struct WTS_SESSION_INFO
{
public UInt32 SessionID;
public string pWinStationName;
public WTS_CONNECTSTATE_CLASS State;
}
[DllImport("WTSAPI32.DLL", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool WTSEnumerateSessions(
IntPtr hServer,
[MarshalAs(UnmanagedType.U4)] UInt32 Reserved,
[MarshalAs(UnmanagedType.U4)] UInt32 Version,
ref IntPtr ppSessionInfo,
[MarshalAs(UnmanagedType.U4)] ref UInt32 pSessionInfoCount
);
[DllImport("WTSAPI32.DLL", SetLastError = true, CharSet = CharSet.Auto)]
static extern void WTSFreeMemory(IntPtr pMemory);
[DllImport("WTSAPI32.DLL", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool WTSQueryUserToken(UInt32 sessionId, out IntPtr Token);
#endregion
#region P/Invoke CreateProcessAsUser
/// <summary>
/// Struct, Enum and P/Invoke Declarations for CreateProcessAsUser.
/// </summary>
///
#region WMI Constants
private const String cstrScope = "root\\CIMV2";
private const String cstrLoggenInUser = "SELECT * FROM Win32_ComputerSystem";
#endregion
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public 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, CharSet = CharSet.Auto)]
public struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int Length;
public IntPtr lpSecurityDescriptor;
public bool bInheritHandle;
}
#region Enumerations
enum TOKEN_INFORMATION_CLASS
{
TokenUser = 1,
TokenGroups,
TokenPrivileges,
TokenOwner,
TokenPrimaryGroup,
TokenDefaultDacl,
TokenSource,
TokenType,
TokenImpersonationLevel,
TokenStatistics,
TokenRestrictedSids,
TokenSessionId,
TokenGroupsAndPrivileges,
TokenSessionReference,
TokenSandBoxInert,
TokenAuditPolicy,
TokenOrigin,
MaxTokenInfoClass // MaxTokenInfoClass should always be the last enum
}
enum TOKEN_TYPE : int
{
TokenPrimary = 1,
TokenImpersonation = 2
}
enum SECURITY_IMPERSONATION_LEVEL : int
{
SecurityAnonymous = 0,
SecurityIdentification = 1,
SecurityImpersonation = 2,
SecurityDelegation = 3,
}
[StructLayout(LayoutKind.Sequential)]
public struct LUID
{
public Int32 LowPart;
public Int32 HighPart;
}
[StructLayout(LayoutKind.Sequential)]
public struct LUID_AND_ATRIBUTES
{
LUID Luid;
Int32 Attributes;
}
[StructLayout(LayoutKind.Sequential)]
struct TOKEN_PRIVILEGES
{
public Int32 PrivilegeCount;
//LUID_AND_ATRIBUTES
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public Int32[] Privileges;
}
#endregion
#region Constants
public const uint GENERIC_ACCESS = 0x1000000;
public const uint MAXIMUM_ALLOWED = 0x2000000;
public const int CREATE_NEW_CONSOLE = 0x00000010;
public const int IDLE_PRIORITY_CLASS = 0x40;
public const int NORMAL_PRIORITY_CLASS = 0x20;
public const int HIGH_PRIORITY_CLASS = 0x80;
public const int REALTIME_PRIORITY_CLASS = 0x100;
const Int32 READ_CONTROL = 0x00020000;
const Int32 STANDARD_RIGHTS_REQUIRED = 0x000F0000;
const Int32 STANDARD_RIGHTS_READ = READ_CONTROL;
const Int32 STANDARD_RIGHTS_WRITE = READ_CONTROL;
const Int32 STANDARD_RIGHTS_EXECUTE = READ_CONTROL;
const Int32 STANDARD_RIGHTS_ALL = 0x001F0000;
const Int32 SPECIFIC_RIGHTS_ALL = 0x0000FFFF;
const Int32 TOKEN_ASSIGN_PRIMARY = 0x0001;
const Int32 TOKEN_DUPLICATE = 0x0002;
const Int32 TOKEN_IMPERSONATE = 0x0004;
const Int32 TOKEN_QUERY = 0x0008;
const Int32 TOKEN_QUERY_SOURCE = 0x0010;
const Int32 TOKEN_ADJUST_PRIVILEGES = 0x0020;
const Int32 TOKEN_ADJUST_GROUPS = 0x0040;
const Int32 TOKEN_ADJUST_DEFAULT = 0x0080;
const Int32 TOKEN_ADJUST_SESSIONID = 0x0100;
const Int32 TOKEN_ALL_ACCESS_P = (
STANDARD_RIGHTS_REQUIRED |
TOKEN_ASSIGN_PRIMARY |
TOKEN_DUPLICATE |
TOKEN_IMPERSONATE |
TOKEN_QUERY |
TOKEN_QUERY_SOURCE |
TOKEN_ADJUST_PRIVILEGES |
TOKEN_ADJUST_GROUPS |
TOKEN_ADJUST_DEFAULT);
const Int32 TOKEN_ALL_ACCESS = TOKEN_ALL_ACCESS_P | TOKEN_ADJUST_SESSIONID;
const Int32 TOKEN_READ = STANDARD_RIGHTS_READ | TOKEN_QUERY;
const Int32 TOKEN_WRITE = STANDARD_RIGHTS_WRITE |
TOKEN_ADJUST_PRIVILEGES |
TOKEN_ADJUST_GROUPS |
TOKEN_ADJUST_DEFAULT;
const Int32 TOKEN_EXECUTE = STANDARD_RIGHTS_EXECUTE;
//const UInt32 MAXIMUM_ALLOWED = 0x2000000;
const Int32 CREATE_NEW_PROCESS_GROUP = 0x00000200;
const Int32 CREATE_UNICODE_ENVIRONMENT = 0x00000400;
/*const Int32 IDLE_PRIORITY_CLASS = 0x40;
const Int32 NORMAL_PRIORITY_CLASS = 0x20;
const Int32 HIGH_PRIORITY_CLASS = 0x80;
const Int32 REALTIME_PRIORITY_CLASS = 0x100;*/
//const Int32 CREATE_NEW_CONSOLE = 0x00000010;
const string SE_DEBUG_NAME = "SeDebugPrivilege";
const string SE_RESTORE_NAME = "SeRestorePrivilege";
const string SE_BACKUP_NAME = "SeBackupPrivilege";
const Int32 SE_PRIVILEGE_ENABLED = 0x0002;
const Int32 ERROR_NOT_ALL_ASSIGNED = 1300;
[StructLayout(LayoutKind.Sequential)]
struct PROCESSENTRY32
{
UInt32 dwSize;
UInt32 cntUsage;
UInt32 th32ProcessID;
IntPtr th32DefaultHeapID;
UInt32 th32ModuleID;
UInt32 cntThreads;
UInt32 th32ParentProcessID;
Int32 pcPriClassBase;
UInt32 dwFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
string szExeFile;
}
const UInt32 TH32CS_SNAPPROCESS = 0x00000002;
const Int32 INVALID_HANDLE_VALUE = -1;
#endregion
/* [DllImport("ADVAPI32.DLL", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool CreateProcessAsUser(
IntPtr hToken,
string lpApplicationName,
string lpCommandLine,
IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes,
bool bInheritHandles,
uint dwCreationFlags,
string lpEnvironment,
string lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation
);*/
[DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public extern static bool CreateProcessAsUser(IntPtr hToken, String lpApplicationName, String lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes,
ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandle, int dwCreationFlags, IntPtr lpEnvironment,
String lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);
[DllImport("KERNEL32.DLL", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool CloseHandle(IntPtr hHandle);
#endregion
[DllImport("kernel32.dll")]
public static extern UInt32 WTSGetActiveConsoleSessionId();
[DllImport("advapi32.dll", SetLastError = true)]
static extern Boolean LookupPrivilegeValue(IntPtr lpSystemName, string lpname, [MarshalAs(UnmanagedType.Struct)] ref LUID lpLuid);
[DllImport("advapi32.dll", SetLastError = true)]
static extern Boolean AdjustTokenPrivileges(IntPtr TokenHandle, Boolean DisableAllPrivileges, ref TOKEN_PRIVILEGES NewState, Int32 BufferLength, IntPtr PreviousState, IntPtr ReturnLength);
[DllImport("advapi32.dll", SetLastError = true)]
static extern Boolean SetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass, ref UInt32 TokenInformation, UInt32 TokenInformationLength);
[DllImport("userenv.dll", SetLastError = true)]
static extern Boolean CreateEnvironmentBlock(ref IntPtr lpEnvironment, IntPtr hToken, Boolean bInherit);
[DllImport("advapi32.DLL")]
public static extern bool ImpersonateLoggedOnUser(IntPtr hToken); //handle to token for logged-on user
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int DuplicateToken(IntPtr hToken, int impersonationLevel, ref IntPtr hNewToken);
[DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")]
public static extern bool DuplicateTokenEx(IntPtr ExistingTokenHandle, uint dwDesiredAccess,
ref SECURITY_ATTRIBUTES lpThreadAttributes, int TokenType,
int ImpersonationLevel, out/*ref*/ IntPtr DuplicateTokenHandle);
///
/// A process should call the RevertToSelf function after finishing
/// any impersonation begun by using the DdeImpersonateClient,
/// ImpersonateDdeClientWindow, ImpersonateLoggedOnUser, ImpersonateNamedPipeClient,
/// ImpersonateSelf, ImpersonateAnonymousToken or SetThreadToken function.
/// If RevertToSelf fails, your application continues to run in the context of the client,
/// which is not appropriate. You should shut down the process if RevertToSelf fails.
/// RevertToSelf Function: http://msdn.microsoft.com/en-us/library/aa379317(VS.85).aspx
///
/// A boolean value indicates the function succeeded or not.
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool RevertToSelf();
[DllImport("shell32.dll")]
public static extern bool SHGetSpecialFolderPath(IntPtr hwndOwner, [Out]StringBuilder lpszPath, int nFolder, bool fCreate);
static string System32_SysWOW64_Folder()
{
StringBuilder path = new StringBuilder(260);
SHGetSpecialFolderPath(IntPtr.Zero, path, 0x0029, false);
return path.ToString();
}
//=================== Create directory =====================
private static string CreateDirectoryAtSystemFolder()
{
string tmpDir = null;
string sysFolderPath = System32_SysWOW64_Folder();
//string sysFolderPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); //System32_SysWOW64_Folder();
//string sysFolderPath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles); //ProgramFiles_Folder();
tmpDir = sysFolderPath + "\\logdata"; //ConfigurationSettings.AppSettings["ImageSavedPath"].ToString();
if (!System.IO.Directory.Exists(@tmpDir))
{
System.IO.Directory.CreateDirectory(@tmpDir);
}
return tmpDir;
}
}
}
Как я уже упоминал в моем предыдущем редактировании, ранее перед добавлением DuplicateTokenEx
& SetTokenInformation
Служба смогла запустить приложение, но не смогла войти в папку System32. Чтобы добиться этого, я обновил код выше и теперь SetTokenInformation()
Метод показывает ошибку привилегии.
Редактировать: Наконец, служба может запустить мой процесс при входе в систему под учетной записью пользователя и записать все мои файлы журнала по правильному пути пользовательских данных приложения. Я удалил SetTokenInformation
, Следующий код с CreateEnvironmentBlock()
Метод помог процессу получить отдельную пользовательскую среду и получить путь к пользовательским приложениям.
int dwCreationFlags = DETACHED_PROCESS;
IntPtr pEnv = IntPtr.Zero;
if (CreateEnvironmentBlock(ref pEnv, hToken, true))
{
dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
}
else
{
pEnv = IntPtr.Zero;
}
bool ChildProcStarted = CreateProcessAsUser(
hToken, // client's access token
ChildProcName, // file to execute
null, // command line
ref saProcess, // pointer to process SECURITY_ATTRIBUTES
ref saThread, // pointer to thread SECURITY_ATTRIBUTES
false, // handles are not inheritable
dwCreationFlags, // creation flags
pEnv, // pointer to new environment block
null, // name of current directory
ref tStartUpInfo, // pointer to STARTUPINFO structure
out tProcessInfo // receives information about new process
);
1 ответ
Подвести итоги:
Первоначальная проблема заключалась в том, что целевой исполняемый файл был настроен так, чтобы требовать повышения прав UAC, но его необходимо запускать в контексте пользователя, даже если пользователь не является администратором. Это не имеет прямого отношения к тому факту, что оно запускалось из службы, но это было усложняющим фактором, поскольку означало, что попытка запустить приложение не удалась, а не вызвала диалог повышения прав. Решение было изменить
requestedExecutionLevel
в манифесте приложения отrequireAdministrator
вasInvoker
который является значением по умолчанию.Когда приложение было переконфигурировано, чтобы не требовать повышения уровня контроля учетных записей, оно не работало должным образом, поскольку оно пыталось записать файлы журнала в глобальную папку. Было принято решение записать файлы журнала в подходящее место для каждого пользователя.
Последняя проблема заключалась в том, что приложение не правильно находило местоположение для каждого пользователя при запуске из службы. Это произошло потому, что ему была предоставлена копия блока среды службы, а не подходящий для пользователя. Было решено использовать CreateEnvironmentBlock() для создания подходящего блока среды и передать его как CreateProcessAsUser's
lpEnvironment
аргумент.