I am getting access violation exceptions when running the code below when the CertFreeCertificateContext method is invoked.
I imagine it is because of the way the pServerCert argument is being Mashalled on the LdapServerCertDelegate but have been unable to find a solution.
using System;
using System.Runtime.InteropServices;
namespace ldaptest
{
class Program
{
static void Main(string[] args)
{
new LdapAuthenticationProvider().AuthenticateUser("a.qas", "a", "administrator", "test123");
}
}
public class LdapAuthenticationProvider
{
public void AuthenticateUser(string server, string domain, string username, string password)
{
IntPtr ld = ldap_sslinit(server, LDAP_SSL_PORT, 1);
if (IntPtr.Zero == ld) throw new Exception("ldap_sslinit");
var version = new IntPtr(LDAP_VERSION3);
var ret = ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, version);
if (ret != LDAP_SUCCESS) throw new Exception(string.Format("LDAP_OPT_PROTOCOL_VERSION 0x{0:X}", ret));
var ldapOn = new IntPtr(LDAP_OPT_ON);
ret = ldap_set_option(ld, LDAP_OPT_SSL, ldapOn);
if (ret != LDAP_SUCCESS) throw new Exception(string.Format("LDAP_OPT_SSL 0x{0:X}", ret));
// note the necessity to convert the delegate to a function pointer
var callback = new LdapServerCertDelegate(AcceptAnySslCertificate);
IntPtr pFn = Marshal.GetFunctionPointerForDelegate(callback);
ret = ldap_set_option(ld, LDAP_OPT_SERVER_CERTIFICATE, pFn);
if (ret != LDAP_SUCCESS) throw new Exception(string.Format("LDAP_OPT_SERVER_CERTIFICATE 0x{0:X}", ret));
var tv = new l_timeval();
ret = ldap_connect(ld, ref tv);
if (ret != LDAP_SUCCESS) throw new Exception(string.Format("ldap_connect 0x{0:X}", ret));
string login = string.Format(#"{0}\{1}", domain, username);
ret = ldap_bind_s(ld, login, password, LDAP_AUTH_SIMPLE); // triggers the callback
if (ret != LDAP_SUCCESS) throw new Exception(string.Format("ldap_bind_s 0x{0:X}", ret));
ldap_unbind_s(ld);
Console.WriteLine("Success");
Console.Read();
}
private delegate bool LdapServerCertDelegate(IntPtr connection, IntPtr pServerCert);
private bool AcceptAnySslCertificate(IntPtr connection, IntPtr pServerCert)
{
CertFreeCertificateContext(pServerCert); // << System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
return true;
}
#region crypt32.dll functions
[DllImport("crypt32.dll", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CertFreeCertificateContext(IntPtr pCertContext);
#endregion
#region Winldap.h definitions
private const int LDAP_PORT = 389;
private const uint LDAP_SSL_PORT = 636;
private const int LDAP_VERSION3 = 3;
private const int LDAP_OPT_PROTOCOL_VERSION = 17;
private const int LDAP_OPT_SSL = 10;
private const int LDAP_OPT_ON = 1;
private const int LDAP_AUTH_SIMPLE = 128;
private const int LDAP_OPT_SERVER_CERTIFICATE = 129;
private const uint LDAP_SUCCESS = 0;
[StructLayoutAttribute(LayoutKind.Sequential)]
private struct l_timeval
{
private int tv_sec;
private int tv_usec;
}
#endregion
#region wldap32.dll functions
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ldap/ldap/ldap_sslinit.asp?frame=true
[DllImport("wldap32.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_sslinitW",
SetLastError = true, CharSet = CharSet.Unicode)]
private static extern IntPtr ldap_sslinit(string hostName, uint portNumber, int secure);
[DllImport("wldap32.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_set_optionW",
SetLastError = true, CharSet = CharSet.Unicode)]
private static extern uint ldap_set_option([In] IntPtr ldapHandle, int option, IntPtr invalue);
[DllImport("wldap32.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_unbind_s",
SetLastError = true, CharSet = CharSet.Unicode)]
private static extern uint ldap_unbind_s([In] IntPtr ldapHandle);
[DllImport("wldap32.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_connect",
SetLastError = true, CharSet = CharSet.Unicode)]
private static extern uint ldap_connect([In] IntPtr ld, [In] ref l_timeval timeout);
[DllImport("wldap32.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_bind_sW",
SetLastError = true, CharSet = CharSet.Unicode)]
private static extern uint ldap_bind_s([In] IntPtr ld, string dn, string cred, uint method);
#endregion
}
}
best bet turned out to be avoiding PInvoke and using C++/CLI.
Related
when I try to inject a DLL with C# into a java process (Minecraft specifically) I get an error "ERROR 6", I've been googling this error but couldnt find a solution.
The program is run in administrator mode, and it isnt trying to load from an admin path.
My code:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
namespace Launcher
{
internal class InjectionUtils
{
public struct CTX_PROCS
{
public Process[] procs;
}
private const int PROCESS_CREATE_THREAD = 2;
private const int PROCESS_QUERY_INFORMATION = 1024;
private const int PROCESS_VM_OPERATION = 8;
private const int PROCESS_VM_WRITE = 32;
private const int PROCESS_VM_READ = 16;
private const uint MEM_COMMIT = 4096u;
private const uint MEM_RESERVE = 8192u;
private const uint PAGE_READWRITE = 4u;
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
private static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)]
private static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, uint nSize, out UIntPtr lpNumberOfBytesWritten);
[DllImport("kernel32.dll")]
private static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int MessageBox(IntPtr hWnd, string text, string caption, uint option);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll")]
private static extern uint GetLastError();
private void MBox(string text)
{
InjectionUtils.MessageBox(IntPtr.Zero, text, "", 0u);
}
public void DoInjection(string dllPath)
{
InjectionUtils.CTX_PROCS cTX_PROCS = this.FindProcessByNameAndTitle("javaw", "Minecraft 1.7");
this.InjectDynamicLib(dllPath, cTX_PROCS.procs[0].Id);
}
public InjectionUtils.CTX_PROCS FindProcessByNameAndTitle(string processName, string title)
{
InjectionUtils.CTX_PROCS result = default(InjectionUtils.CTX_PROCS);
List<Process> list = new List<Process>();
Process[] processes = Process.GetProcesses();
for (int i = 0; i < processes.Length; i++)
{
Process process = processes[i];
if (process.ProcessName.Equals(processName) && process.MainWindowTitle.Contains(title))
{
list.Add(process);
}
}
result.procs = list.ToArray();
return result;
}
public void eject()
{
}
private bool InjectDynamicLib(string dllPath, int pId)
{
bool result = false;
IntPtr intPtr = InjectionUtils.OpenProcess(1082, false, pId);
uint num = (uint)((dllPath.Length + 1) * Marshal.SizeOf(typeof(char)));
if (intPtr != IntPtr.Zero)
{
IntPtr procAddress = InjectionUtils.GetProcAddress(InjectionUtils.GetModuleHandle("kernel32.dll"), "LoadLibraryA");
IntPtr intPtr2 = InjectionUtils.VirtualAllocEx(intPtr, IntPtr.Zero, num, 12288u, 4u);
UIntPtr uIntPtr;
// Write path of dll to remote process
if (InjectionUtils.WriteProcessMemory(intPtr, intPtr2, Encoding.Default.GetBytes(dllPath), num, out uIntPtr))
{
InjectionUtils.CloseHandle(InjectionUtils.CreateRemoteThread(intPtr, IntPtr.Zero, 0u, procAddress, intPtr2, 0u, IntPtr.Zero));
result = true;
}
InjectionUtils.CloseHandle(intPtr);
}
if (InjectionUtils.GetLastError() != 0u)
{
this.MBox("ERROR " + InjectionUtils.GetLastError());
}
return result;
}
If anyone could help I would appreciate it dearly :)
Thanks in advance :D
From MSDN Error Code List:
ERROR_INVALID_HANDLE
0x6
Before you call GetLastError() you call CloseHandle() twice, once on the thread handle and on the process handle.
To find the problem just check the returns from OpenProcess() and CreateRemoteThread() and compare against the info you find in the functions documentation on MSDN.
For a simple no frills C# injector, this is the code I like to use which includes some error checking and uses the managed Process::Dispose() method instead of calling the native CloseHandle() at least on the process handle:
public static bool InjectDLL(string dllpath, string procname)
{
Process[] procs = Process.GetProcessesByName(Path.GetFileNameWithoutExtension(procname));
if (procs.Length == 0)
{
return false;
}
Process proc = procs[0];
if (proc.Handle != IntPtr.Zero)
{
IntPtr loc = VirtualAllocEx(proc.Handle, IntPtr.Zero, MAX_PATH, AllocationType.Commit | AllocationType.Reserve,
MemoryProtection.ReadWrite);
if (loc.Equals(0))
{
return false;
}
IntPtr bytesRead = IntPtr.Zero;
bool result = WriteProcessMemory(proc.Handle, loc, dllpath.ToCharArray(), dllpath.Length, out bytesRead);
if (!result || bytesRead.Equals(0))
{
return false;
}
IntPtr loadlibAddy = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
IntPtr hThread = CreateRemoteThread(proc.Handle, IntPtr.Zero, 0, loadlibAddy, loc, 0, out _);
if (!hThread.Equals(0))
{
CloseHandle(hThread);
}
else return false;
}
else return false;
//this will CloseHandle automatically using the managed method
proc.Dispose();
return true;
}
Make sure to run as admin and pinvoke any missing definitions.
I'm trying to read the memory of a process.
The actual code loops through the process' memory and searches for values but this is the general idea.
I'm compiling for x64 and attempting to read x64 processes.
This code fails after the call to VirtualProtectEx with either error code 5 (ERROR_ACCESS_DENIED) or error code 487 (ERROR_INVALID_ADDRESS) depending on the process selected.
Am I reading the process' memory in the correct way?
How can VirtualProtectEx fail with access denied?
Are there other protection methods I haven't considered?
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;
namespace ReadProcessMemoryTest {
public class Program {
public static void Main(string[] args) {
string processName = "ProcessName";
IntPtr startAddress = new IntPtr(0x00000000);
IntPtr endAddress = new IntPtr(0x7FFFFFFF);
uint bytesToRead = 8;
int errorCode = 0;
// Ensure running as admin
WindowsIdentity identity = WindowsIdentity.GetCurrent();
WindowsPrincipal principal = new WindowsPrincipal(identity);
if(!principal.IsInRole(WindowsBuiltInRole.Administrator)){
throw new Exception("Not running as administrator");
}
// Turn on SeDebugPrivilege
Process.EnterDebugMode();
// Select the process
Process process = Process.GetProcessesByName(processName)[0];
// Get a handle to the process with all access rights
IntPtr processHandle = OpenProcess(0x001F0FFF, 1, (uint)process.Id);
// Check for errors
errorCode = Marshal.GetLastWin32Error();
if(errorCode != 0) {
throw new Exception("OpenProcess error: " + errorCode);
}
// Set the protection level of these 8 bytes to execute, read and write
uint prevProtection = 0;
VirtualProtectEx(processHandle, startAddress, new UIntPtr(bytesToRead), 0x40, out prevProtection);
// Check for errors
errorCode = Marshal.GetLastWin32Error();
if(errorCode != 0) {
throw new Exception("VirtualProtectEx error: " + errorCode);
}
// Read some bytes into an array
byte[] buffer = new byte[bytesToRead];
IntPtr bytesRead;
ReadProcessMemory(processHandle, startAddress, buffer, bytesToRead, out bytesRead);
// Check for errors
errorCode = Marshal.GetLastWin32Error();
if(errorCode != 0) {
throw new Exception("ReadProcessMemory error: " + errorCode);
}
// Close the process handle
CloseHandle(processHandle);
}
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr OpenProcess(UInt32 dwDesiredAccess, Int32 bInheritHandle, UInt32 dwProcessId);
[DllImport("kernel32.dll")]
public static extern Int32 CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern Int32 VirtualProtectEx(IntPtr hProcess, IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern Int32 ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [In, Out] byte[] buffer, UInt32 size, out IntPtr lpNumberOfBytesRead);
}
}
EnterDebugMode() is not enough, you need to explicitly AdjustTokenPrivileges
using System;
using System.Runtime.InteropServices;
public class TokenManipulator
{
[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
internal static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall,
ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen);
[DllImport("kernel32.dll", ExactSpelling = true)]
internal static extern IntPtr GetCurrentProcess();
[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
internal static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok);
[DllImport("advapi32.dll", SetLastError = true)]
internal static extern bool LookupPrivilegeValue(string host, string name, ref long pluid);
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct TokPriv1Luid
{
public int Count;
public long Luid;
public int Attr;
}
internal const int SE_PRIVILEGE_DISABLED = 0x00000000;
internal const int SE_PRIVILEGE_ENABLED = 0x00000002;
internal const int TOKEN_QUERY = 0x00000008;
internal const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
public static bool AddPrivilege(string privilege)
{
try
{
bool retVal;
TokPriv1Luid tp;
IntPtr hproc = GetCurrentProcess();
IntPtr htok = IntPtr.Zero;
retVal = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok);
tp.Count = 1;
tp.Luid = 0;
tp.Attr = SE_PRIVILEGE_ENABLED;
retVal = LookupPrivilegeValue(null, privilege, ref tp.Luid);
retVal = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
return retVal;
}
catch (Exception ex)
{
throw ex;
}
}
I'm trying to get the Clipboard data using this native method with C#/.NET. The problem is I'm mangling the data. Here's my code:
IntPtr pointer = GetClipboardData(dataformat);
int size = Marshal.SizeOf(pointer);
byte[] buff = new byte[size];
Marshal.Copy(data, buff, 0, size);
Here's the pinvoke I'm using for the GetClipboardData method:
[DllImport("user32.dll")]
private static extern IntPtr GetClipboardData(uint uFormat);
Can anyone tell me where I'm going wrong?
If you want to get byte array from clipboard, which represents unicode text for example, the code will be something like this:
using System;
using System.Runtime.InteropServices;
using System.Text;
public class ClipboardHelper
{
#region Win32
[DllImport("User32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool IsClipboardFormatAvailable(uint format);
[DllImport("User32.dll", SetLastError = true)]
private static extern IntPtr GetClipboardData(uint uFormat);
[DllImport("User32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool OpenClipboard(IntPtr hWndNewOwner);
[DllImport("User32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseClipboard();
[DllImport("Kernel32.dll", SetLastError = true)]
private static extern IntPtr GlobalLock(IntPtr hMem);
[DllImport("Kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GlobalUnlock(IntPtr hMem);
[DllImport("Kernel32.dll", SetLastError = true)]
private static extern int GlobalSize(IntPtr hMem);
private const uint CF_UNICODETEXT = 13U;
#endregion
public static string GetText()
{
if (!IsClipboardFormatAvailable(CF_UNICODETEXT))
return null;
try
{
if (!OpenClipboard(IntPtr.Zero))
return null;
IntPtr handle = GetClipboardData(CF_UNICODETEXT);
if (handle == IntPtr.Zero)
return null;
IntPtr pointer = IntPtr.Zero;
try
{
pointer = GlobalLock(handle);
if (pointer == IntPtr.Zero)
return null;
int size = GlobalSize(handle);
byte[] buff = new byte[size];
Marshal.Copy(pointer, buff, 0, size);
return Encoding.Unicode.GetString(buff).TrimEnd('\0');
}
finally
{
if (pointer != IntPtr.Zero)
GlobalUnlock(handle);
}
}
finally
{
CloseClipboard();
}
}
}
I'm trying to set an already installed windows service to automatic delayed start in C#. How do I set a windows service to
Automatic (Delayed Start)
Can't find that value in the ServiceStartMode enum.
Edit:1
public class ServiceAutoStartInfo
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private struct SERVICE_DELAYED_AUTO_START_INFO
{
public bool fDelayedAutostart;
}
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool ChangeServiceConfig2(IntPtr hService, int dwInfoLevel, IntPtr lpInfo);
// Service configuration parameter
const int SERVICE_CONFIG_DELAYED_AUTO_START_INFO = 3;
public bool ChangeDelayedAutoStart(IntPtr hService, bool delayed)
{
// Validate service handle
if (hService != IntPtr.Zero)
{
// Create
SERVICE_DELAYED_AUTO_START_INFO info = new SERVICE_DELAYED_AUTO_START_INFO();
// Set the DelayedAutostart property
info.fDelayedAutostart = delayed;
// Allocate necessary memory
IntPtr hInfo = Marshal.AllocHGlobal(Marshal.SizeOf(
typeof(SERVICE_DELAYED_AUTO_START_INFO)));
// Convert structure to pointer
Marshal.StructureToPtr(info, hInfo, true);
// Change the configuration
bool result = ChangeServiceConfig2(hService, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, hInfo);
// Release memory
Marshal.FreeHGlobal(hInfo);
return result;
}
return false;
}
}
This is how I call it:
var controller = new ServiceController(s.ServiceName);
var autoDelay = new ServiceAutoStartInfo();
autoDelay.ChangeDelayedAutoStart(controller.ServiceHandle.DangerousGetHandle(), true);
But with no result.
Look into calling the Windows ChangeServiceConfig2 function, with dwInfoLevel of SERVICE_CONFIG_DELAYED_AUTO_START_INFO and a SERVICE_DELAYED_AUTO_START_INFO struct with fDelayedAutostart set to TRUE.
Or, you can do this with the command line:
sc.exe config <servicename> start= delayed-auto
I'm using the following, which works for me on Windows 7 (when run as admin):
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.ServiceProcess;
namespace ServiceManager
{
/// <summary>
/// Extensions to the ServiceController class.
/// </summary>
public static class ServiceControlerExtensions
{
/// <summary>
/// Set the start mode for the service.
/// </summary>
/// <param name="serviceController">The service controller.</param>
/// <param name="mode">The desired start mode.</param>
public static void SetStartMode(this ServiceController serviceController, ServiceStartModeEx mode)
{
IntPtr serviceManagerHandle = OpenServiceManagerHandle();
IntPtr serviceHandle = OpenServiceHandle(serviceController, serviceManagerHandle);
try
{
if (mode == ServiceStartModeEx.DelayedAutomatic)
{
ChangeServiceStartType(serviceHandle, ServiceStartModeEx.Automatic);
ChangeDelayedAutoStart(serviceHandle, true);
}
else
{
// Delayed auto-start overrides other settings, so it must be set first.
ChangeDelayedAutoStart(serviceHandle, false);
ChangeServiceStartType(serviceHandle, mode);
}
}
finally
{
if (serviceHandle != IntPtr.Zero)
{
CloseServiceHandle(serviceHandle);
}
if (serviceHandle != IntPtr.Zero)
{
CloseServiceHandle(serviceManagerHandle);
}
}
}
private static IntPtr OpenServiceHandle(ServiceController serviceController, IntPtr serviceManagerHandle)
{
var serviceHandle = OpenService(
serviceManagerHandle,
serviceController.ServiceName,
SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG);
if (serviceHandle == IntPtr.Zero)
{
throw new ExternalException("Open Service Error");
}
return serviceHandle;
}
private static IntPtr OpenServiceManagerHandle()
{
IntPtr serviceManagerHandle = OpenSCManager(null, null, SC_MANAGER_ALL_ACCESS);
if (serviceManagerHandle == IntPtr.Zero)
{
throw new ExternalException("Open Service Manager Error");
}
return serviceManagerHandle;
}
private static void ChangeServiceStartType(IntPtr serviceHandle, ServiceStartModeEx mode)
{
bool result = ChangeServiceConfig(
serviceHandle,
SERVICE_NO_CHANGE,
(uint)mode,
SERVICE_NO_CHANGE,
null,
null,
IntPtr.Zero,
null,
null,
null,
null);
if (result == false)
{
ThrowLastWin32Error("Could not change service start type");
}
}
private static void ChangeDelayedAutoStart(IntPtr hService, bool delayed)
{
// Create structure that contains DelayedAutoStart property.
SERVICE_DELAYED_AUTO_START_INFO info = new SERVICE_DELAYED_AUTO_START_INFO();
// Set the DelayedAutostart property in that structure.
info.fDelayedAutostart = delayed;
// Allocate necessary memory.
IntPtr hInfo = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SERVICE_DELAYED_AUTO_START_INFO)));
// Convert structure to pointer.
Marshal.StructureToPtr(info, hInfo, true);
// Change the configuration.
bool result = ChangeServiceConfig2(hService, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, hInfo);
// Release memory.
Marshal.FreeHGlobal(hInfo);
if (result == false)
{
ThrowLastWin32Error("Could not set service to delayed automatic");
}
}
private static void ThrowLastWin32Error(string messagePrefix)
{
int nError = Marshal.GetLastWin32Error();
var win32Exception = new Win32Exception(nError);
string message = string.Format("{0}: {1}", messagePrefix, win32Exception.Message);
throw new ExternalException(message);
}
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr OpenService(
IntPtr hSCManager,
string lpServiceName,
uint dwDesiredAccess);
[DllImport("advapi32.dll", EntryPoint = "OpenSCManagerW", ExactSpelling = true, CharSet = CharSet.Unicode,
SetLastError = true)]
private static extern IntPtr OpenSCManager(
string machineName,
string databaseName,
uint dwAccess);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern Boolean ChangeServiceConfig(
IntPtr hService,
UInt32 nServiceType,
UInt32 nStartType,
UInt32 nErrorControl,
String lpBinaryPathName,
String lpLoadOrderGroup,
IntPtr lpdwTagId,
[In] char[] lpDependencies,
String lpServiceStartName,
String lpPassword,
String lpDisplayName);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool ChangeServiceConfig2(
IntPtr hService,
int dwInfoLevel,
IntPtr lpInfo);
[DllImport("advapi32.dll", EntryPoint = "CloseServiceHandle")]
private static extern int CloseServiceHandle(IntPtr hSCObject);
private const uint SERVICE_NO_CHANGE = 0xFFFFFFFF;
private const uint SERVICE_QUERY_CONFIG = 0x00000001;
private const uint SERVICE_CHANGE_CONFIG = 0x00000002;
private const uint SC_MANAGER_ALL_ACCESS = 0x000F003F;
private const int SERVICE_CONFIG_DELAYED_AUTO_START_INFO = 3;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private struct SERVICE_DELAYED_AUTO_START_INFO
{
public bool fDelayedAutostart;
}
}
}
namespace ServiceManager
{
public enum ServiceStartModeEx
{
Automatic = 2,
Manual = 3,
Disabled = 4,
DelayedAutomatic = 99
}
}
You call it like this:
var serviceController = new ServiceController("Windows Update");
try
{
serviceController.SetStartMode(ServiceStartModeEx.DelayedAutomatic);
}
finally
{
serviceController.Close();
}
Update: This only works for setting up new services and is not what the OP asked for:
You can use the DelayedAutoStart property of the ServiceInstaller.
installer.DelayedAutoStart = true;
I believe you need to combine both methods ChangeServiceConfig and ChangeServiceConfig2.
pseudo-code follows:
public static void ChangeServiceStartupType(ServiceStartupType type, ...)
{
if (type == AutomaticDelayed)
{
if (ChangeServiceConfig2(.., DelayedAutoStart, ..))
{
ChangeServiceConfig(.., Automatic, ..);
}
}
else
{
ChangeServiceConfig2(.., !DelayedAutoStart, ..)
ChangeServiceConfig(.., type, ..)
}
}
edit: you also need to remove "delayed-automatic" when requesting non-delayed startup-type. Otherwise it won't be possible to set "automatic" type. ("automatic-delayed" overrides "automatic")
SERVICE_DELAYED_AUTO_START_INFO structure documentation states:
fDelayedAutostart
If this member is TRUE, the service is started after other auto-start
services are started plus a short delay. Otherwise, the service is
started during system boot.
This setting is ignored unless the service is an auto-start service.
https://learn.microsoft.com/en-us/windows/win32/api/winsvc/ns-winsvc-service_delayed_auto_start_info?redirectedfrom=MSDN#members
So I think if the service is not auto-start it won't change it to auto delayed, you would have to use sc.exe
How do I programmatically set the "Log On" credentials for an arbitrary Windows service using c# (wmi/interop is fine)?
Note, my program is running as an administrator and I need the change to persist (for all subsequent service restarts)
Ideally the method has the following sig:
void SetWindowsServiceCreds(string serviceName, string username, string password)
{
// TODO write me
}
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ChangeServiceConfig(IntPtr hService, UInt32 nServiceType, UInt32 nStartType, UInt32 nErrorControl, String lpBinaryPathName, String lpLoadOrderGroup, IntPtr lpdwTagId, String lpDependencies, String lpServiceStartName, String lpPassword, String lpDisplayName);
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess);
[DllImport("advapi32.dll", EntryPoint = "OpenSCManagerW", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern IntPtr OpenSCManager(
string machineName,
string databaseName,
uint dwAccess);
[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool CloseServiceHandle(IntPtr hSCObject);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern Boolean QueryServiceConfig(IntPtr hService, IntPtr intPtrQueryConfig, UInt32 cbBufSize, out UInt32 pcbBytesNeeded);
[StructLayout(LayoutKind.Sequential)]
public class QUERY_SERVICE_CONFIG
{
[MarshalAs(System.Runtime.InteropServices.UnmanagedType.U4)]
public UInt32 dwServiceType;
[MarshalAs(System.Runtime.InteropServices.UnmanagedType.U4)]
public UInt32 dwStartType;
[MarshalAs(System.Runtime.InteropServices.UnmanagedType.U4)]
public UInt32 dwErrorControl;
[MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
public String lpBinaryPathName;
[MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
public String lpLoadOrderGroup;
[MarshalAs(System.Runtime.InteropServices.UnmanagedType.U4)]
public UInt32 dwTagID;
[MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
public String lpDependencies;
[MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
public String lpServiceStartName;
[MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
public String lpDisplayName;
};
private const uint SC_MANAGER_ALL_ACCESS = 0x000F003F;
private const uint SERVICE_QUERY_CONFIG = 0x00001;
private const uint SERVICE_CHANGE_CONFIG = 0x00002;
private const uint SERVICE_NO_CHANGE = 0xffffffff;
private const int ERROR_INSUFFICIENT_BUFFER = 122;
public static void SetWindowsServiceCreds(string serviceName, string username, string password)
{
IntPtr hManager = IntPtr.Zero;
IntPtr hService = IntPtr.Zero;
try
{
hManager = OpenSCManager(null, null, SC_MANAGER_ALL_ACCESS);
if (hManager == IntPtr.Zero)
{
ThrowWin32Exception();
}
hService = OpenService(hManager, serviceName, SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG);
if (hService == IntPtr.Zero)
{
ThrowWin32Exception();
}
if (!ChangeServiceConfig(hService, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, null, null, IntPtr.Zero, null, username, password, null))
{
ThrowWin32Exception();
}
}
finally
{
if (hService != IntPtr.Zero) CloseServiceHandle(hService);
if (hManager != IntPtr.Zero) CloseServiceHandle(hManager);
}
}
private static void ThrowWin32Exception()
{
int error = Marshal.GetLastWin32Error();
Win32Exception e = new Win32Exception(error);
throw e;
}
This works as well:
void SetWindowsServiceCreds(string serviceName, string username, string password)
{
string objPath = string.Format("Win32_Service.Name='{0}'", serviceName);
using (ManagementObject service = new ManagementObject(new ManagementPath(objPath)))
{
object[] wmiParams = new object[10];
wmiParams[6] = username;
wmiParams[7] = password;
service.InvokeMethod("Change", wmiParams);
}
}