I am trying to start another process with Process.Start running under different credentials with the UAC turned on. I get the following error:
System.ComponentModel.Win32Exception:
Logon failure: user account
restriction. Possible reasons are
blank passwords not allowed, logon
hour restrictions, or a policy
restriction has been enforced at
System.Diagnostics.Process.StartWithCreateProcess(ProcessStartInfo
startInfo) at
System.Diagnostics.Process.Start()
at
System.Diagnostics.Process.Start(ProcessStartInfo
startInfo)
If I turn off UAC, it works fine. If I start the process without specifying username and password, it works fine. Below is a copy of the code:
ProcessStartInfo processInfo = new ProcessStartInfo();
processInfo.Verb = "runas";
processInfo.FileName = command;
processInfo.Arguments = parameters;
processInfo.UserName = txtUserName.Text;
processInfo.Password = password;
processInfo.Domain = "myDomain";
processInfo.UseShellExecute = false;
processInfo.LoadUserProfile = true;
Process.Start(processInfo);
I believe this has something to do with the desktop session, but am not sure why it works when UAC is off.
Any ideas on how to get this to work without turning off UAC, modifying the registry, or changing local/group policies would greatly be appreciated.
Thanks
EDIT
The Process that has the code above is started as an admin:
static void Main(string[] args)
{
//make sure we are running with admin privileges
if (VerifyIsAdmin())
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MyApp(args));
}
}
private static bool VerifyIsAdmin()
{
WindowsPrincipal principal = new WindowsPrincipal(WindowsIdentity.GetCurrent());
bool isAdmin = principal.IsInRole(WindowsBuiltInRole.Administrator);
if (!isAdmin)
{
ProcessStartInfo processInfo = new ProcessStartInfo();
processInfo.Verb = "runas";
processInfo.FileName = Application.ExecutablePath;
Process.Start(processInfo);
}
return isAdmin;
}
EDIT
Tried CreateProcessWithLogonW ... Had same problem.
After further searching, looks like it is a problem with the Group Policy requiring smart card logon (scforceoption). That being said, I still don't understand why it works when UAC is off. It should still require smart card on logon.
It doesn't look like I am going to be able to get around this. Any insight/advice would be appreciated.
You have to do following things:
1. Get who is logged in and in this user has an active desktop
2. Get process' handle started by this user
3. "Steal" credentials from this process
4. Start process with those credentials
Following code should do the trick:
using System;
using System.Text;
using System.Security;
using System.Management;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace Win32
{
public class Win32API
{
#region WMI Constants
private const String cstrScope = "root\\CIMV2";
private const String cstrLoggenInUser = "SELECT * FROM Win32_ComputerSystem";
#endregion
#region Win32 API routines
[StructLayout(LayoutKind.Sequential)]
struct SECURITY_ATTRIBUTES
{
public Int32 Length;
public IntPtr lpSecurityDescriptor;
public Boolean bInheritHandle;
}
enum TOKEN_TYPE
{
TokenPrimary = 1,
TokenImpersonation = 2
}
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
}
[StructLayout(LayoutKind.Sequential)]
struct STARTUPINFO
{
public Int32 cb;
public String lpReserved;
public String lpDesktop;
public String lpTitle;
public UInt32 dwX;
public UInt32 dwY;
public UInt32 dwXSize;
public UInt32 dwYSize;
public UInt32 dwXCountChars;
public UInt32 dwYCountChars;
public UInt32 dwFillAttribute;
public UInt32 dwFlags;
public short wShowWindow;
public short cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public UInt32 dwProcessId;
public UInt32 dwThreadId;
}
enum SECURITY_IMPERSONATION_LEVEL
{
SecurityAnonymous = 0,
SecurityIdentification = 1,
SecurityImpersonation = 2,
SecurityDelegation = 3,
}
[StructLayout(LayoutKind.Sequential)]
struct LUID
{
public Int32 LowPart;
public Int32 HighPart;
}
[StructLayout(LayoutKind.Sequential)]
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;
}
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;
[DllImport("kernel32.dll", SetLastError = true)]
static extern Boolean CloseHandle(IntPtr hSnapshot);
[DllImport("kernel32.dll")]
public static extern UInt32 WTSGetActiveConsoleSessionId();
[DllImport("Wtsapi32.dll")]
static extern UInt32 WTSQueryUserToken(UInt32 SessionId, ref IntPtr phToken);
[DllImport("advapi32.dll", SetLastError = true)]
static extern Boolean LookupPrivilegeValue(IntPtr lpSystemName, string lpname, [MarshalAs(UnmanagedType.Struct)] ref LUID lpLuid);
[DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
extern static Boolean CreateProcessAsUser(IntPtr hToken, String lpApplicationName, String lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes,
ref SECURITY_ATTRIBUTES lpThreadAttributes, Boolean bInheritHandle, Int32 dwCreationFlags, IntPtr lpEnvironment,
String lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);
[DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")]
extern static Boolean DuplicateTokenEx(IntPtr ExistingTokenHandle, UInt32 dwDesiredAccess,
ref SECURITY_ATTRIBUTES lpThreadAttributes, Int32 TokenType,
Int32 ImpersonationLevel, ref IntPtr DuplicateTokenHandle);
[DllImport("kernel32.dll")]
static extern IntPtr OpenProcess(UInt32 dwDesiredAccess, Boolean bInheritHandle, UInt32 dwProcessId);
[DllImport("advapi32", SetLastError = true), SuppressUnmanagedCodeSecurityAttribute]
static extern Boolean OpenProcessToken(IntPtr ProcessHandle, // handle to process
Int32 DesiredAccess, // desired access to process
ref IntPtr TokenHandle); // handle to open access token
[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);
#endregion
#region Methods
/// <summary>
/// Method returns name of the user that logged in on workstation
/// </summary>
public static String GetLoggedInUserName()
{
String userName = String.Empty;
try
{
ManagementObjectSearcher searcher =
new ManagementObjectSearcher(cstrScope, cstrLoggenInUser);
foreach (ManagementObject queryObj in searcher.Get())
{
userName = queryObj["UserName"].ToString();
break;
}
}
catch
{
userName = String.Empty;
}
return userName;
}
/// <summary>
/// Creates the process in the interactive desktop with credentials of the logged in user.
/// </summary>
public static Boolean CreateProcessAsUser(String commandLine,
String workingDirectory,
String userAppName,
out StringBuilder output)
{
Boolean processStarted = false;
output = new StringBuilder();
try
{
UInt32 dwSessionId = WTSGetActiveConsoleSessionId();
output.AppendLine(String.Format("Active console session Id: {0}", dwSessionId));
IntPtr hUserToken = IntPtr.Zero;
WTSQueryUserToken(dwSessionId, ref hUserToken);
if (hUserToken != IntPtr.Zero)
{
output.AppendLine(String.Format("WTSQueryUserToken() OK (hUserToken:{0})", hUserToken));
Process[] processes = Process.GetProcessesByName(userAppName);
if (processes.Length == 0)
{
output.AppendLine(String.Format("Application '{0}' can not be found in the running processes", userAppName));
return false;
}
Int32 userAppProcessId = -1;
for (Int32 k = 0; k < processes.Length; k++)
{
output.AppendLine(String.Format("Process: '{0}', PID: {1}, Handle: {2}, Session: {3}",
processes[k].ProcessName, processes[k].Id, processes[k].Handle, processes[k].SessionId));
if ((UInt32)processes[k].SessionId == dwSessionId)
{
userAppProcessId = processes[k].Id;
}
}
if (userAppProcessId == -1)
{
output.AppendLine(String.Format("Application '{0}' is not found in the processes of the current session", userAppName));
return false;
}
IntPtr hProcess = OpenProcess((Int32)MAXIMUM_ALLOWED, false, (UInt32)userAppProcessId);
IntPtr hPToken = IntPtr.Zero;
OpenProcessToken(hProcess,
TOKEN_ADJUST_PRIVILEGES
| TOKEN_QUERY
| TOKEN_DUPLICATE
| TOKEN_ASSIGN_PRIMARY
| TOKEN_ADJUST_SESSIONID
| TOKEN_READ
| TOKEN_WRITE,
ref hPToken);
if (hPToken != IntPtr.Zero)
{
output.AppendLine(String.Format("OpenProcessToken() OK (Token: {0})", hPToken));
LUID luid = new LUID();
if (LookupPrivilegeValue(IntPtr.Zero, SE_DEBUG_NAME, ref luid))
{
output.AppendLine(String.Format("LookupPrivilegeValue() OK (High: {0}, Low: {1})", luid.HighPart, luid.LowPart));
SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
sa.Length = Marshal.SizeOf(sa);
IntPtr hUserTokenDup = IntPtr.Zero;
DuplicateTokenEx(hPToken,
(Int32)MAXIMUM_ALLOWED,
ref sa,
(Int32)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,
(Int32)TOKEN_TYPE.TokenPrimary,
ref hUserTokenDup);
if (hUserTokenDup != IntPtr.Zero)
{
output.AppendLine(String.Format("DuplicateTokenEx() OK (hToken: {0})", hUserTokenDup));
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(hUserTokenDup,
TOKEN_INFORMATION_CLASS.TokenSessionId,
ref dwSessionId,
(UInt32)IntPtr.Size))
{
output.AppendLine(String.Format("SetTokenInformation() OK"));
if (AdjustTokenPrivileges(hUserTokenDup,
false, ref tp, Marshal.SizeOf(tp),
IntPtr.Zero, IntPtr.Zero))
{
output.AppendLine("AdjustTokenPrivileges() OK");
Int32 dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;
IntPtr pEnv = IntPtr.Zero;
if (CreateEnvironmentBlock(ref pEnv, hUserTokenDup, true))
{
dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
}
else
{
output.AppendLine(String.Format("CreateEnvironmentBlock() FAILED (Last Error: {0})", Marshal.GetLastWin32Error()));
pEnv = IntPtr.Zero;
}
// Launch the process in the client's logon session.
PROCESS_INFORMATION pi;
STARTUPINFO si = new STARTUPINFO();
si.cb = Marshal.SizeOf(si);
si.lpDesktop = "winsta0\\default";
output.AppendLine(String.Format("CreateProcess (Path:{0}, CurrDir:{1})", commandLine, workingDirectory));
if (CreateProcessAsUser(hUserTokenDup, // client's access token
null, // file to execute
commandLine, // command line
ref sa, // pointer to process SECURITY_ATTRIBUTES
ref sa, // pointer to thread SECURITY_ATTRIBUTES
false, // handles are not inheritable
dwCreationFlags, // creation flags
pEnv, // pointer to new environment block
workingDirectory, // name of current directory
ref si, // pointer to STARTUPINFO structure
out pi // receives information about new process
))
{
processStarted = true;
output.AppendLine(String.Format("CreateProcessAsUser() OK (PID: {0})", pi.dwProcessId));
}
else
{
output.AppendLine(String.Format("CreateProcessAsUser() failed (Last Error: {0})", Marshal.GetLastWin32Error()));
}
}
else
{
output.AppendLine(String.Format("AdjustTokenPrivileges() failed (Last Error: {0})", Marshal.GetLastWin32Error()));
}
}
else
{
output.AppendLine(String.Format("SetTokenInformation() failed (Last Error: {0})", Marshal.GetLastWin32Error()));
}
CloseHandle(hUserTokenDup);
}
else
{
output.AppendLine(String.Format("DuplicateTokenEx() failed (Last Error: {0})", Marshal.GetLastWin32Error()));
}
}
else
{
output.AppendLine(String.Format("LookupPrivilegeValue() failed (Last Error: {0})", Marshal.GetLastWin32Error()));
}
CloseHandle(hPToken);
}
else
{
output.AppendLine(String.Format("OpenProcessToken() failed (Last Error: {0})", Marshal.GetLastWin32Error()));
}
CloseHandle(hUserToken);
}
else
{
output.AppendLine(String.Format("WTSQueryUserToken failed: {0}", Marshal.GetLastWin32Error()));
}
}
catch (Exception ex)
{
output.AppendLine("Exception occurred: " + ex.Message);
}
return processStarted;
}
#endregion
}
}
Usage:
try
{
StringBuilder output = new StringBuilder();
if (!Win32API.CreateProcessAsUser("C:\\WINDOWS\\notepad.exe", "C:\\WINDOWS\\", "winlogon", out output))
throw new Win32Exception(output.ToString());
else
throw new Win32Exception("Process RUN!!!");
}
catch (Win32Exception ex)
{
File.WriteAllText("c:\\hello!.txt", ex.Message + " " + ex.ErrorCode.ToString());
}
Credits for the code goes to Forcas from rsdn.ru
I found some old project where I used code below and it worked at the time (looks similar to yours), maybe it won't help but still it's worth a try :
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.UseShellExecute = true;
startInfo.WorkingDirectory = Environment.CurrentDirectory;
startInfo.FileName = Application.ExecutablePath;
startInfo.Verb = "runas";
try
{
Process p = Process.Start(startInfo);
}
catch(System.ComponentModel.Win32Exception ex)
{
return;
}
It looks like you aren't alone with this issue, and it may not be possible as you are trying to do:
http://www.icodefactory.com/lab/post/UAC-Revealed-7e-elevation-of-rights-from-NET-as-commonly-misunderstood.aspx
It's just a guess, but maybe you can request admin privileges and then impersonate the user and then finally then start the process while impersonating?
Edit: You can also try PInvoke which based on a comment on this page is working for starting a process under a different user:
http://blogs.msdn.com/thottams/archive/2006/08/11/696013.aspx
Try adding manifest file for that other process that you want to start.
Windows will analyze manifest file and present you with UAC dialog before process start.
Here is the info how to embed manifest file link1 and link2
HTH
Related
I've been trying to disable/enable Windows 10 devices immidiately with WPF. There is an answer here but it give me an exception as mentioned in the answer's comments. There also an suggestion for fixing also in the answer's comments but I don't know how to make it work because I'm new to WPF.
Here is the code:
public static class DisableHardware
{
const uint DIF_PROPERTYCHANGE = 0x12;
const uint DICS_ENABLE = 1;
const uint DICS_DISABLE = 2; // disable device
const uint DICS_FLAG_GLOBAL = 1; // not profile-specific
const uint DIGCF_ALLCLASSES = 4;
const uint DIGCF_PRESENT = 2;
const uint ERROR_INVALID_DATA = 13;
const uint ERROR_NO_MORE_ITEMS = 259;
const uint ERROR_ELEMENT_NOT_FOUND = 1168;
static DEVPROPKEY DEVPKEY_Device_DeviceDesc;
static DEVPROPKEY DEVPKEY_Device_HardwareIds;
[StructLayout(LayoutKind.Sequential)]
struct SP_CLASSINSTALL_HEADER
{
public UInt32 cbSize;
public UInt32 InstallFunction;
}
[StructLayout(LayoutKind.Sequential)]
struct SP_PROPCHANGE_PARAMS
{
public SP_CLASSINSTALL_HEADER ClassInstallHeader;
public UInt32 StateChange;
public UInt32 Scope;
public UInt32 HwProfile;
}
[StructLayout(LayoutKind.Sequential)]
struct SP_DEVINFO_DATA
{
public UInt32 cbSize;
public Guid classGuid;
public UInt32 devInst;
public IntPtr reserved; // CHANGE #1 - was UInt32
}
[StructLayout(LayoutKind.Sequential)]
struct DEVPROPKEY
{
public Guid fmtid;
public UInt32 pid;
}
[DllImport("setupapi.dll", SetLastError = true)]
static extern IntPtr SetupDiGetClassDevsW(
[In] ref Guid ClassGuid,
[MarshalAs(UnmanagedType.LPWStr)]
string Enumerator,
IntPtr parent,
UInt32 flags);
[DllImport("setupapi.dll", SetLastError = true)]
static extern bool SetupDiDestroyDeviceInfoList(IntPtr handle);
[DllImport("setupapi.dll", SetLastError = true)]
static extern bool SetupDiEnumDeviceInfo(IntPtr deviceInfoSet,
UInt32 memberIndex,
[Out] out SP_DEVINFO_DATA deviceInfoData);
[DllImport("setupapi.dll", SetLastError = true)]
static extern bool SetupDiSetClassInstallParams(
IntPtr deviceInfoSet,
[In] ref SP_DEVINFO_DATA deviceInfoData,
[In] ref SP_PROPCHANGE_PARAMS classInstallParams,
UInt32 ClassInstallParamsSize);
[DllImport("setupapi.dll", SetLastError = true)]
static extern bool SetupDiChangeState(
IntPtr deviceInfoSet,
[In] ref SP_DEVINFO_DATA deviceInfoData);
[DllImport("setupapi.dll", SetLastError = true)]
static extern bool SetupDiGetDevicePropertyW(
IntPtr deviceInfoSet,
[In] ref SP_DEVINFO_DATA DeviceInfoData,
[In] ref DEVPROPKEY propertyKey,
[Out] out UInt32 propertyType,
IntPtr propertyBuffer,
UInt32 propertyBufferSize,
out UInt32 requiredSize,
UInt32 flags);
[DllImport("setupapi.dll", SetLastError = true)]
static extern bool SetupDiGetDeviceRegistryPropertyW(
IntPtr DeviceInfoSet,
[In] ref SP_DEVINFO_DATA DeviceInfoData,
UInt32 Property,
[Out] out UInt32 PropertyRegDataType,
IntPtr PropertyBuffer,
UInt32 PropertyBufferSize,
[In,Out] ref UInt32 RequiredSize
);
static DisableHardware()
{
DisableHardware.DEVPKEY_Device_DeviceDesc = new DEVPROPKEY();
DEVPKEY_Device_DeviceDesc.fmtid = new Guid(
0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67,
0xd1, 0x46, 0xa8, 0x50, 0xe0);
DEVPKEY_Device_DeviceDesc.pid = 2;
DEVPKEY_Device_HardwareIds = new DEVPROPKEY();
DEVPKEY_Device_HardwareIds.fmtid = new Guid(
0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67,
0xd1, 0x46, 0xa8, 0x50, 0xe0);
DEVPKEY_Device_HardwareIds.pid = 3;
}
public static void DisableDevice(Func<string, bool> filter, bool disable = true)
{
IntPtr info = IntPtr.Zero;
Guid NullGuid = Guid.Empty;
try
{
info = SetupDiGetClassDevsW(
ref NullGuid,
null,
IntPtr.Zero,
DIGCF_ALLCLASSES);
CheckError("SetupDiGetClassDevs");
SP_DEVINFO_DATA devdata = new SP_DEVINFO_DATA();
devdata.cbSize = (UInt32)Marshal.SizeOf(devdata);
// Get first device matching device criterion.
for (uint i = 0; ; i++)
{
SetupDiEnumDeviceInfo(info,
i,
out devdata);
// if no items match filter, throw
if (Marshal.GetLastWin32Error() == ERROR_NO_MORE_ITEMS)
CheckError("No device found matching filter.", 0xcffff);
CheckError("SetupDiEnumDeviceInfo");
string devicepath = GetStringPropertyForDevice(info,
devdata, 1); // SPDRP_HARDWAREID
// Uncomment to print name/path
//Console.WriteLine(GetStringPropertyForDevice(info,
// devdata, DEVPKEY_Device_DeviceDesc));
//Console.WriteLine(" {0}", devicepath);
if (devicepath != null && filter(devicepath)) break;
}
SP_CLASSINSTALL_HEADER header = new SP_CLASSINSTALL_HEADER();
header.cbSize = (UInt32)Marshal.SizeOf(header);
header.InstallFunction = DIF_PROPERTYCHANGE;
SP_PROPCHANGE_PARAMS propchangeparams = new SP_PROPCHANGE_PARAMS();
propchangeparams.ClassInstallHeader = header;
propchangeparams.StateChange = disable ? DICS_DISABLE : DICS_ENABLE;
propchangeparams.Scope = DICS_FLAG_GLOBAL;
propchangeparams.HwProfile = 0;
SetupDiSetClassInstallParams(info,
ref devdata,
ref propchangeparams,
(UInt32)Marshal.SizeOf(propchangeparams));
CheckError("SetupDiSetClassInstallParams");
SetupDiChangeState(
info,
ref devdata);
CheckError("SetupDiChangeState");
}
finally
{
if (info != IntPtr.Zero)
SetupDiDestroyDeviceInfoList(info);
}
}
private static void CheckError(string message, int lasterror = -1)
{
int code = lasterror == -1 ? Marshal.GetLastWin32Error() : lasterror;
if (code != 0)
throw new ApplicationException(
String.Format("Error disabling hardware device (Code {0}): {1}",
code, message));
}
private static string GetStringPropertyForDevice(IntPtr info, SP_DEVINFO_DATA devdata,
uint propId)
{
uint proptype, outsize;
IntPtr buffer = IntPtr.Zero;
try
{
uint buflen = 512;
buffer = Marshal.AllocHGlobal((int)buflen);
outsize=0;
// CHANGE #2 - Use this instead of SetupDiGetDeviceProperty
SetupDiGetDeviceRegistryPropertyW(
info,
ref devdata,
propId,
out proptype,
buffer,
buflen,
ref outsize);
byte[] lbuffer = new byte[outsize];
Marshal.Copy(buffer, lbuffer, 0, (int)outsize);
int errcode = Marshal.GetLastWin32Error();
if (errcode == ERROR_INVALID_DATA) return null;
CheckError("SetupDiGetDeviceProperty", errcode);
return Encoding.Unicode.GetString(lbuffer);
}
finally
{
if (buffer != IntPtr.Zero)
Marshal.FreeHGlobal(buffer);
}
}
}
The suggested fix is:
The problem is that, the function uses a fixed buffer length of 512, which a lot of times is not enough to hold the data. What needs to be done is to first get the size of buffer as follows: if(!SetupDiGetDeviceRegistryPropertyW(info, ref devdata, propId, out proptype, IntPtr.Zero, 0, ref outsize)). Once the size is got, allocate the buffer and call SetupDiGetDeviceRegistryPropertyW again
I haven't messed with this stuff before, but just reading the comment on that other question, it looks like the suggestion is to do this:
private static string GetStringPropertyForDevice(IntPtr info, SP_DEVINFO_DATA devdata, uint propId)
{
uint proptype, outsize;
IntPtr buffer = IntPtr.Zero;
try
{
SetupDiGetDeviceRegistryPropertyW(
info,
ref devdata,
propId,
out proptype,
IntPtr.Zero,
0,
ref outsize);
uint buflen = outsize;
buffer = Marshal.AllocHGlobal((int)buflen);
outsize = 0;
SetupDiGetDeviceRegistryPropertyW(
info,
ref devdata,
propId,
out proptype,
buffer,
buflen,
ref outsize);
byte[] lbuffer = new byte[outsize];
Marshal.Copy(buffer, lbuffer, 0, (int)outsize);
int errcode = Marshal.GetLastWin32Error();
if (errcode == ERROR_INVALID_DATA) return null;
CheckError("SetupDiGetDeviceProperty", errcode);
return Encoding.Unicode.GetString(lbuffer);
}
finally
{
if (buffer != IntPtr.Zero)
Marshal.FreeHGlobal(buffer);
}
}
Call SetupDiGetDeviceRegistryPropertyW with no buffer first, just to get the outsize value, then use that as your buffer size. This avoids creating a buffer of arbitrary length which could be exceeded, at the cost of making the call twice.
Again- I haven't tested this or anything, it just seems like what the person writing that comment was intending. Hopefully it works!
I'm trying to configure a device's TCP/IP settings via registries but i can't seem to get it to work without a soft reboot.
I've been scouring Google for an answer and found that the way to do it is to pinvoke DeviceIoControl().
I've looked at the registries and they are definitely changing to the ones i want, I can't get DeviceIoControl() to return anything except 0 (fail) and I'm not sure what I'm doing wrong.
[DllImport("coredll.dll", EntryPoint = "CreateFile", SetLastError = true)]
private static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFLagsAndAttributes, IntPtr hTemplateFile);
[DllImport("coredll.dll", EntryPoint = "DeviceIoControl", SetLastError = true)]
private static extern int DeviceIoControl(IntPtr hDevice, uint dwIoControlCode, ref string lpInBuffer, int nInBufferSize, out string lpOutBuffer, int nOutBufferSize, ref int lpBytesReturned, IntPtr lpOverlapped);
[DllImport("coredll.dll", EntryPoint = "CloseHandle", SetLastError = true)]
private static extern bool CloseHandle(IntPtr hObject);
public static void SetTCPIP()
{
RegistryKey baseKey = Registry.LocalMachine;
string subKey = #"Comm\FEC1\Parms\TcpIp";
string enableDhcpKey = "EnableDHCP";
string gatewayKey = "DefaultGateway";
string ipAddressKey = "IpAddress";
string subnetMaskKey = "Subnetmask";
string ipAddress = "192.168.8.100";
string subnetMask = "255.255.255.0";
string gateway = "192.168.8.1";
WriteReg(baseKey, subKey, enableDhcpKey, 0);
WriteRegMultiString(baseKey, subKey, ipAddressKey, ipAddress);
WriteRegMultiString(baseKey, subKey, gatewayKey, gateway);
WriteRegMultiString(baseKey, subKey, subnetMaskKey, subnetMask);
CommitLocalMachineRegistry(); //RegFlushKey()
BindAdapter();
}
private static void BindAdapter()
{
const uint genericRead = 0x80000000;
const uint genericWrite = 0x40000000;
const int IOCTL_NDIS_REBIND_ADAPTER = 0x17002E;
const int OPEN_EXISTING = 3;
const string NI = #"NDS0:";
//Open the Handle to Rebind the Network Adapter
IntPtr hNdis = CreateFile(NI, genericRead | genericWrite, 0, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
if (hNdis == IntPtr.Zero)
{
Debug.WriteLine("!!!!!!!!!!!!!!!!!"+hNdis);
}
string Adapter = "FEC1\0";
string OutBuffer = "";
int bytesReturned = 0;
//Rebind the Network Adapter
int check = DeviceIoControl(hNdis, IOCTL_NDIS_REBIND_ADAPTER, ref Adapter, Adapter.Length,
out OutBuffer, 0, ref bytesReturned, IntPtr.Zero);
if (check==0)
{
Debug.WriteLine("!!!!!!!!!!!!!!!!!!");
Debug.WriteLine(Marshal.GetLastWin32Error());
}
//Close the Handle
CloseHandle(hNdis);
}
I've tried getting more info on the issue using GetLastWin32Error() but it's returning 0 as well
Edit (couldn't add comment)
i've tried having Adapter = "FEC1\0\0" and I've also tried marshalling as a StringBuilder with no luck
Edit 2
Registries are HIVE based, so changes persist after a reboot.
I want to launch an external program from C# to be completely detached.
I use CreateProcess through pinvoke because Process.Start doesn't allow me to use DETACHED_PROCESS. Also I want this application to redirect it's output to some file.
Here's the sample code:
var processInformation = new ProcessUtility.PROCESS_INFORMATION();
var securityInfo = new ProcessUtility.STARTUPINFO();
var sa = new ProcessUtility.SECURITY_ATTRIBUTES();
sa.Length = Marshal.SizeOf(sa);
// Create process with no window and totally detached
var result = ProcessUtility.CreateProcess(Path.Combine(Environment.SystemDirectory, "cmd.exe"), commandLineArguments, ref sa, ref sa, false,
ProcessUtility.DETACHED_PROCESS, IntPtr.Zero, null, ref securityInfo, out processInformation);
CommandLineArguments are something like this : "/c Foo.bat > Foo.log 2>&1"
Everything works fine and Foo.log is populated by Foo.bat. No additional console window is visible. PERFECT.
CommandLineArguments are something like this : "/c Foo.exe > Foo.log 2>&1"
Foo.exe is .NET Console Application.
Foo.log is not populated and Foo.exe is launched in visible console window. STRANGE. Why behavior is different from 1. ?
Just for your information. CommandLineArguments are something like this : "/c Foo.exe > Foo.log 2>&1"
Foo.exe is .NET Windows Application.
Everything works fine but when I launch this application simply from command prompt I see no output because no console is allocated.
I want 2. work the same as 1. Why there's a difference ?
UPDATE: I don't want to write the Foo.log for myself because launching application will be killed.
UPDATE: Ok, I wrote some code to specify that only one handle is inherited but CreateProcess gives me error 87 when calling with EXTENDED_STARTUPINFO_PRESENT ( even if it's present and empty ).
Can you please help me why?
public class ProcessUtility
{
// Process creation flags
const uint ZERO_FLAG = 0x00000000;
const uint CREATE_BREAKAWAY_FROM_JOB = 0x01000000;
const uint CREATE_DEFAULT_ERROR_MODE = 0x04000000;
const uint CREATE_NEW_CONSOLE = 0x00000010;
const uint CREATE_NEW_PROCESS_GROUP = 0x00000200;
const uint CREATE_NO_WINDOW = 0x08000000;
const uint CREATE_PROTECTED_PROCESS = 0x00040000;
const uint CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000;
const uint CREATE_SEPARATE_WOW_VDM = 0x00001000;
const uint CREATE_SHARED_WOW_VDM = 0x00001000;
const uint CREATE_SUSPENDED = 0x00000004;
const uint CREATE_UNICODE_ENVIRONMENT = 0x00000400;
const uint DEBUG_ONLY_THIS_PROCESS = 0x00000002;
const uint DEBUG_PROCESS = 0x00000001;
const uint DETACHED_PROCESS = 0x00000008;
const uint EXTENDED_STARTUPINFO_PRESENT = 0x00080000;
const uint INHERIT_PARENT_AFFINITY = 0x00010000;
// Thread attributes flags
const uint PROC_THREAD_ATTRIBUTE_HANDLE_LIST = 0x00020002;
const uint PROC_THREAD_ATTRIBUTE_PARENT_PROCESS = 0x00020000;
// File creation flags
const uint FILE_ACCESS_WRITE = 0x40000000;
// StartupInfo flags
const int STARTF_USESTDHANDLES = 0x00000100;
[StructLayout(LayoutKind.Sequential)]
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 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 STARTUPINFOEX
{
public STARTUPINFO StartupInfo;
public IntPtr lpAttributeList;
};
[StructLayout(LayoutKind.Sequential)]
struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public Int32 dwProcessID;
public Int32 dwThreadID;
}
[StructLayout(LayoutKind.Sequential)]
struct SECURITY_ATTRIBUTES
{
public Int32 Length;
public IntPtr lpSecurityDescriptor;
public bool bInheritHandle;
}
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool CreateProcess(
string lpApplicationName,
string lpCommandLine,
ref SECURITY_ATTRIBUTES lpProcessAttributes,
ref SECURITY_ATTRIBUTES lpThreadAttributes,
bool bInheritHandles,
uint dwCreationFlags,
IntPtr lpEnvironment,
string lpCurrentDirectory,
[In] ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool CreateProcess(
string lpApplicationName,
string lpCommandLine,
ref SECURITY_ATTRIBUTES lpProcessAttributes,
ref SECURITY_ATTRIBUTES lpThreadAttributes,
bool bInheritHandles,
uint dwCreationFlags,
IntPtr lpEnvironment,
string lpCurrentDirectory,
[In] ref STARTUPINFOEX lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
[DllImport("kernel32.dll")]
public static extern uint GetLastError();
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UpdateProcThreadAttribute(
IntPtr lpAttributeList, uint dwFlags, IntPtr Attribute, IntPtr lpValue,
IntPtr cbSize, IntPtr lpPreviousValue, IntPtr lpReturnSize);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool InitializeProcThreadAttributeList(
IntPtr lpAttributeList, int dwAttributeCount, int dwFlags, ref IntPtr lpSize);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool DeleteProcThreadAttributeList(IntPtr lpAttributeList);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)]
static extern SafeFileHandle CreateFile(
string lpFileName,
uint fileAccess,
[MarshalAs(UnmanagedType.U4)] FileShare fileShare,
SECURITY_ATTRIBUTES securityAttributes,
[MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
uint dwFlagsAndAttributes,
IntPtr hTemplateFile);
public static bool CreateProcessWithStdHandlesRedirect(string lpApplicationName, string lpCommandLine, string logFilename)
{
var startupInfo = new STARTUPINFOEX();
startupInfo.StartupInfo.cb = Marshal.SizeOf(startupInfo);
try
{
var lpSize = IntPtr.Zero;
if (InitializeProcThreadAttributeList(IntPtr.Zero, 1, 0, ref lpSize) || lpSize == IntPtr.Zero)
return false;
startupInfo.lpAttributeList = Marshal.AllocHGlobal(lpSize);
// Here startupInfo.lpAttributeList is initialized to hold 1 value
if (!InitializeProcThreadAttributeList(startupInfo.lpAttributeList, 1, 0, ref lpSize))
return false;
var fileSecurityAttributes = new SECURITY_ATTRIBUTES();
fileSecurityAttributes.Length = Marshal.SizeOf(fileSecurityAttributes);
// Create inheritable file handle
fileSecurityAttributes.bInheritHandle = true;
// Open log file for writing
using (var handle = CreateFile(logFilename, FILE_ACCESS_WRITE, FileShare.ReadWrite,
fileSecurityAttributes, FileMode.Create, 0, IntPtr.Zero))
{
var fileHandle = handle.DangerousGetHandle();
// Add filehandle to proc thread attribute list
if (!UpdateProcThreadAttribute(startupInfo.lpAttributeList, 0, (IntPtr)PROC_THREAD_ATTRIBUTE_HANDLE_LIST, fileHandle,
(IntPtr)IntPtr.Size, IntPtr.Zero, IntPtr.Zero))
return false;
startupInfo.StartupInfo.hStdError = fileHandle;
startupInfo.StartupInfo.hStdOutput = fileHandle;
// startupInfo.StartupInfo.hStdInput = ?;
startupInfo.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
var processInformation = new PROCESS_INFORMATION();
var securityAttributes = new SECURITY_ATTRIBUTES();
securityAttributes.Length = Marshal.SizeOf(securityAttributes);
securityAttributes.bInheritHandle = true;
// Create process with no window and totally detached
return ProcessUtility.CreateProcess(lpApplicationName, lpCommandLine, ref securityAttributes, ref securityAttributes, true,
DETACHED_PROCESS | EXTENDED_STARTUPINFO_PRESENT, IntPtr.Zero, null, ref startupInfo, out processInformation);
}
}
finally
{
if (startupInfo.lpAttributeList != IntPtr.Zero)
{
DeleteProcThreadAttributeList(startupInfo.lpAttributeList);
Marshal.FreeHGlobal(startupInfo.lpAttributeList);
}
}
}
}
In case 1, the instance of cmd.exe that you're launching can run the batch file itself. No child process is created.
In case 2, the instance of cmd.exe that you're launching has to run the console application as a child process. It has no way of knowing that you wanted the application to not be given a console window, so when it calls CreateProcess it does not use the DETACHED_PROCESS flag, and Windows creates a new console as normal.
In case 3, the child process is not a console application, so Windows does not create a console for it even though DETACHED_PROCESS was not specified.
The usual solution is to open the foo.log file yourself, launch the console application directly (rather than via cmd.exe) and use the STARTUP_INFO structure to pass the log file handle as the standard output and standard error for the new process. Once CreateProcess has returned you can close the file handle. The duplicated handle in the child process will not be affected when your process closes.
However, I'm not sure how you would properly go about that in .NET. It's a bit tricky at the best of times, because you have to make the child process inherit the log file handle without having it inherit other handles inappropriately - that's probably the reason Process.Start is causing you problems. The recommended practice is to use a process/thread attribute list (InitializeProcThreadAttributeList) with a PROC_THREAD_ATTRIBUTE_HANDLE_LIST entry. (But the log handle still needs to be inheritable.)
Create a vb script, NoWindow.vbs, perhaps programmatically on the fly, as follows
CreateObject("Wscript.Shell").Run WScript.Arguments(0), 0, False
From you main app, call cscript simply with Process.Start
Process.Start("cscript", "NoWindow.vbs \"cmd /c Foo.exe > Foo.log 2>&1 \" ");
The vbs script itself will detach the process. No window is visible.
My testing was limited to using Procexp to confirm that the proceses Foo.exe was detached - Win7.
Chekout this answer to hide console:
How to Run a C# console application with the console hidden
And this answer to launch detached process :
Process Tree
Following this link, I have implemented WTSQueryUserToken in my C# solution and called the CreateProcessAsUserWrapper.LaunchChildProcess("app_path") method from the OnStart of my Windows Service which is made to run as "LocalSystem". It is able to start the process which can interact with desktop, but for Windows Professional and not for Windows Ultimate. I tried it on Windows Professional 64 bit and it is able to successfully start the process on user log on interactively, but on Windows Ultimate 64 bit version the CreateProcessAsUser method is returning false and the service can be seen running at SCM whereas the process is not.
My process is a win form app which needs to run with currently logged on user account for desktop interaction and uses the InteropServices. Can it be a framework issue? How to get to the problem? Please help.
Edit:
Both the machines are running Windows 7 64-bit. I added the following code to get the error code after CreateProcessAsUser and it returns "The requested operation requires elevation" as error.
string errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
It is happening only in Windows 7 Ultimate and not in Windows 7 Professional. I need to run my process for every logged on user and that is why I think I can't use the CreateProcessWithLogonW method instead. How can I fix this elevation privilege issue now?
Edit:
There was 2 app.manifest files, one inside the service project and the other inside the win form (process) project. I removed both the files, built the msi with WiX and tested again. Now it is successfully running on every machine, the process being started with every logged on user account. But, one problem is caused now. Actually I need to write some log under the windows/system folder, which I can't do now. I am getting "UnauthorizedAccessException" thrown from the process. Moreover, no user except administrator is allowed to stop or uninstall the process, but in this case a user can start the Task Manager and end the process from there since it is running with the corresponding user account. Is it possible to run the process with the user account but with elevated privilege, so that I can write my log under windows/system folder and prevent the user from stopping the process without admin permission? At least resolving the first issue i.e writing log files under system folder is very important for me.
Edit:
After adding SetTokenInformation along with DuplicateTokenEx my service fails to start the app with the log "SetTokenInformation() returns FALSE. ERROR: A required privilege is not held by the client". I am providing the full code, please see if I have updated it correctly with all the steps.
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;
}
}
}
As I mentioned in my earlier edit, previously before adding DuplicateTokenEx & SetTokenInformation, the service was able to run the app but it could not log under System32 folder. To achieve that I updated to the above code and now the SetTokenInformation() method shows the privilege error.
Edit: Finally the service could start my process on logged on user account and write all my log files at proper user appdata path. I have removed SetTokenInformation. Following code with CreateEnvironmentBlock() method helped the process get a separate user environment and retrieve the user appdata path.
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
);
To summarize:
The original problem was that the target executable was configured to require UAC elevation, but must be run in the user's context, even if the user is not an administrator. This isn't directly related to the fact that it was being launched from a service, but that was a complicating factor because it meant that the attempt to launch the application failed rather than resulting in an elevation dialog. The resolution was to change requestedExecutionLevel in the application's manifest from requireAdministrator to asInvoker which is the default value.
When the application was reconfigured to not require UAC elevation, it failed to run properly because it was trying to write log files to a global folder. The resolution was to write the log files to a suitable per-user location.
The last issue was that the application was not finding the per-user location correctly when launched from the service. This was because it was being given a copy of the service's environment block rather than one suitable to the user. The resolution was to use CreateEnvironmentBlock() to generate a suitable environment block and pass it as CreateProcessAsUser's lpEnvironment argument.
I am trying to port the following C++ code:
BOOL SyskeyGetClassBytes(HKEY hKeyReg,LPSTR keyName,LPSTR valueName,LPBYTE classBytes) {
HKEY hKey,hSubKey;
DWORD dwDisposition=0,classSize;
BYTE classStr[16];
LONG ret;
BOOL isSuccess = FALSE;
ret = RegCreateKeyEx(hKeyReg,keyName,0,NULL,REG_OPTION_NON_VOLATILE,KEY_QUERY_VALUE,NULL,&hKey,&dwDisposition);
if(ret!=ERROR_SUCCESS)
return FALSE;
else if(dwDisposition!=REG_OPENED_EXISTING_KEY) {
RegCloseKey(hKey);
return FALSE;
}
else {
if(RegOpenKeyEx(hKey,valueName,0,KEY_READ,&hSubKey)==ERROR_SUCCESS) {
classSize = 8+1;
ret = RegQueryInfoKey(hSubKey,(LPTSTR)classStr,&classSize,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
if((ret==ERROR_SUCCESS)&&(classSize==8)) {
classBytes[0]= (HexDigitToByte(classStr[0]) << 4) | HexDigitToByte(classStr[1]);
classBytes[1]= (HexDigitToByte(classStr[2]) << 4) | HexDigitToByte(classStr[3]);
classBytes[2]= (HexDigitToByte(classStr[4]) << 4) | HexDigitToByte(classStr[5]);
classBytes[3]= (HexDigitToByte(classStr[6]) << 4) | HexDigitToByte(classStr[7]);
isSuccess = TRUE;
}
RegCloseKey(hSubKey);
}
RegCloseKey(hKey);
}
return isSuccess;
}
I spent like 5 hours trying to figure out my problem, with no success. I know for a fact that I am properly calling this method. My C# code is
unsafe static bool SyskeyGetClassBytes(RegistryHive hKeyReg, string keyName, string valueName, byte* classBytes)
{
UIntPtr hSubKey;
UIntPtr hKey;
RegResult tmp; ;
uint classSize;
StringBuilder classStr = new StringBuilder();
int ret;
bool isSuccess = false;
ret = RegCreateKeyEx(hKeyReg, keyName, 0, null, RegOption.NonVolatile, RegSAM.QueryValue, UIntPtr.Zero, out hKey, out tmp);
if (ret != 0)
{
return false;
}
else if (tmp != RegResult.OpenedExistingKey)
{
return false;
}
else
{
int res = RegOpenKeyEx(hKey, valueName, 0, (int)RegSAM.Read, out hSubKey);
if (res == 0)
{
classSize = 8 + 1;
ret = RegQueryInfoKey(hSubKey, out classStr, ref classSize, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
if ((classSize == 8))
{
classBytes[0] = (byte)((byte)(HexDigitToByte(classStr[0]) << (byte)4) | HexDigitToByte(classStr[1]));
classBytes[1] = (byte)((byte)(HexDigitToByte(classStr[2]) << (byte)4) | HexDigitToByte(classStr[3]));
classBytes[2] = (byte)((byte)(HexDigitToByte(classStr[4]) << (byte)4) | HexDigitToByte(classStr[5]));
classBytes[3] = (byte)((byte)(HexDigitToByte(classStr[6]) << (byte)4) | HexDigitToByte(classStr[7]));
isSuccess = true;
}
RegCloseKey(hSubKey);
}
else
{
return false;
}
RegCloseKey(hKey);
}
return isSuccess;
}
Its a little bit hard for me to debug, but eventually I determined that the problem is occurring at this line. Execution seems to halt afterwards.
ret = RegQueryInfoKey(hSubKey, out classStr, ref classSize, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
I know this is not a problem with permissions, as this C# program is running with admin perms AND as the local system account. The method that I need that the .Net APIs don't offer is RegQueryInfoKey. My P/Invoke signatures and types used are:
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int nLength;
public unsafe byte* lpSecurityDescriptor;
public int bInheritHandle;
}
[Flags]
public enum RegOption
{
NonVolatile = 0x0,
Volatile = 0x1,
CreateLink = 0x2,
BackupRestore = 0x4,
OpenLink = 0x8
}
[Flags]
public enum RegSAM
{
QueryValue = 0x0001,
SetValue = 0x0002,
CreateSubKey = 0x0004,
EnumerateSubKeys = 0x0008,
Notify = 0x0010,
CreateLink = 0x0020,
WOW64_32Key = 0x0200,
WOW64_64Key = 0x0100,
WOW64_Res = 0x0300,
Read = 0x00020019,
Write = 0x00020006,
Execute = 0x00020019,
AllAccess = 0x000f003f
}
public enum RegResult
{
CreatedNewKey = 0x00000001,
OpenedExistingKey = 0x00000002
}
[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
public static extern int RegOpenKeyEx(
UIntPtr hKey,
string subKey,
int ulOptions,
int samDesired,
out UIntPtr hkResult);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern int RegCloseKey(
UIntPtr hKey);
[DllImport("advapi32.dll", SetLastError = true)]
static extern int RegCreateKeyEx(
RegistryHive hKey,
string lpSubKey,
int Reserved,
string lpClass,
RegOption dwOptions,
RegSAM samDesired,
UIntPtr lpSecurityAttributes,
out UIntPtr phkResult,
out RegResult lpdwDisposition);
[DllImport("advapi32.dll", EntryPoint = "RegQueryInfoKey", CallingConvention = CallingConvention.Winapi, SetLastError = true)]
extern private static int RegQueryInfoKey(
UIntPtr hkey,
out StringBuilder lpClass,
ref uint lpcbClass,
IntPtr lpReserved,
IntPtr lpcSubKeys,
IntPtr lpcbMaxSubKeyLen,
IntPtr lpcbMaxClassLen,
IntPtr lpcValues,
IntPtr lpcbMaxValueNameLen,
IntPtr lpcbMaxValueLen,
IntPtr lpcbSecurityDescriptor,
IntPtr lpftLastWriteTime);
The lpClass parameter is declared incorrectly. Pass the StringBuilder by value.
[DllImport("advapi32.dll")]
extern private static int RegQueryInfoKey(
UIntPtr hkey,
StringBuilder lpClass,
ref uint lpcbClass,
IntPtr lpReserved,
IntPtr lpcSubKeys,
IntPtr lpcbMaxSubKeyLen,
IntPtr lpcbMaxClassLen,
IntPtr lpcValues,
IntPtr lpcbMaxValueNameLen,
IntPtr lpcbMaxValueLen,
IntPtr lpcbSecurityDescriptor,
IntPtr lpftLastWriteTime
);
You also need to allocate the StringBuilder instance to have the desired capacity. So, allocate the StringBuilder like this:
StringBuilder classStr = new StringBuilder(255);//or whatever length you like
And then set classSize like this:
classSize = classStr.Capacity+1;
I removed the parameters to DllImport. Most are not necessary, and the SetLastError is incorrect.
There may be other issues with your code, but with these changes at least the call to RegQueryInfoKey will match your C++ code.
Try this signature for RegQueryInfoKey:
[DllImport("advapi32.dll", EntryPoint="RegQueryInfoKey", CallingConvention=CallingConvention.Winapi, SetLastError=true)]
extern private static int RegQueryInfoKey(
UIntPtr hkey,
out StringBuilder lpClass,
ref uint lpcbClass,
IntPtr lpReserved,
out uint lpcSubKeys,
out uint lpcbMaxSubKeyLen,
out uint lpcbMaxClassLen,
out uint lpcValues,
out uint lpcbMaxValueNameLen,
out uint lpcbMaxValueLen,
out uint lpcbSecurityDescriptor,
IntPtr lpftLastWriteTime);
You are not declaring them as out params and in the RegQueryInfoKey Win32 call they are _Out_opt_.
You need to initialize your StringBuilder with enough capacity to store a classSize number of characters.
classSize = 8 + 1;
classStr.Capacity = classSize;
ret = RegQueryInfoKey(hSubKey, out classStr, ref classSize, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
The marshaller will use the capacity set on the StringBuilder to send a buffer of the capacity size to the RegQueryInfoKey function. Without that you are probably corrupted memory.
See http://msdn.microsoft.com/en-us/library/s9ts558h.aspx#cpcondefaultmarshalingforstringsanchor3