I am working on a WCF service that is hosted as a Windows service that uses named pipes - NamedPipeServerStream (user privilege) to establish a secure connection between server and client processes. To verify the authenticity of the client process, I need to verify the digital signature of the client process executable, and therefore I am trying to get the client executable path using its process identifier.
I am using Windows 7 Professional SP1 (64-bit) OS and Visual Studio 2015 Community Edition for development. Both the server (Windows service) and client processes (other exe) are built only in Release x64 mode.
When some client process is connected to the server, I try to get the path of the client's EXE process, but it throws an βAccess Errorβ in the following line of code:
return Process.GetProcessById(processId).MainModule.FileName;
Therefore, in order to solve this problem, I ran into an error and tried some other experimental tests, as described below, which also did not seem to work.
Experimental tests
- Tried to get a client process handler with access rights to the request, but failed β Always returns 0 and the last Win32 error as 5 ( Access denied )
OpenProcess(ProcessAccessFlags.PROCESS_QUERY_INFORMATION, false, processId);
- I tried to get a handle to the client process with limited access rights to the request, but failed β Always returns 0 and the last Win32 error as 5 ( Access denied )
OpenProcess(ProcessAccessFlags.PROCESS_QUERY_LIMITED_INFORMATION, false, processId);
- Install SeDebugPrivilege and continue to handle the client process descriptor with a limited request privilege, but failed β SeDebugPrivilege is enabled, but still returns 0 for the process descriptor and the last Win32 error as 5 ( Access denied ).
- I tried to set the administrator privilege for the Windows service using the app.manifest file (requireAdministrator attribute), but failed β Access is denied
- I tried using ManagementObject to get the properties of the process information, but failed β The object link is not installed in the object instance
- The login account for the Windows service has been changed as "Local System" from "Local Service", but failed
However, I tried all of the above methods in the example Windows console application, which works fine without any errors, and the same does not work in the Windows service. I also tried to convert the console application to dll and transfer it to the server to get information about the client process, but failed β again Access denied
I absolutely do not understand what is happening and how to solve it. Your suggestions will be very helpful.
EDIT:. Locate the code snippet for OpenProcess and ProcessAccessFlags, as shown below:
public static string GetProcessExecutablePath(int processId) { try { string exePath = string.Empty; //If running on Vista or later use the new function if (Environment.OSVersion.Version.Major >= 6) { return GetProcessExecutablePathAboveVista(processId); } return Process.GetProcessById(processId).MainModule.FileName; } catch (Exception ex) { return "Exception in GetProcessExecutablePath(): " + ex.Message + ": " + ex.InnerException; } } private static string GetProcessExecutablePathAboveVista(int processId) { var buffer = new StringBuilder(1024); IntPtr hprocess = NativeMethods.OpenProcess(NativeMethods.ProcessAccessFlags.PROCESS_QUERY_LIMITED_INFORMATION, false, processId); if (hprocess != IntPtr.Zero) { try { int size = buffer.Capacity; if (NativeMethods.QueryFullProcessImageName(hprocess, 0, buffer, out size)) { return buffer.ToString(); } else { return "Failed in QueryFullProcessImageName(): " + Marshal.GetLastWin32Error();; } } catch (Exception ex) { return "Exception in: " + ex.Message; } finally { NativeMethods.CloseHandle(hprocess); } } else { return "Handle is Zero: " + Marshal.GetLastWin32Error(); } }
NativeMethods.cs
[Flags] public enum ProcessAccessFlags { PROCESS_TERMINATE = 0x0001, PROCESS_CREATE_THREAD = 0x0002, PROCESS_VM_OPERATION = 0x0008, PROCESS_VM_READ = 0x0010, PROCESS_VM_WRITE = 0x0020, PROCESS_DUP_HANDLE = 0x0040, PROCESS_CREATE_PROCESS = 0x0080, PROCESS_SET_QUOTA = 0x0100, PROCESS_SET_INFORMATION = 0x0200, PROCESS_QUERY_INFORMATION = 0x0400, PROCESS_SUSPEND_RESUME = 0x0800, PROCESS_QUERY_LIMITED_INFORMATION = 0x1000, SYNCHRONIZE = 0x100000, DELETE = 0x00010000, READ_CONTROL = 0x00020000, WRITE_DAC = 0x00040000, WRITE_OWNER = 0x00080000, STANDARD_RIGHTS_REQUIRED = 0x000F0000, PROCESS_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFFF } [DllImport("Kernel32.dll", EntryPoint = "OpenProcess", SetLastError = true)] public static extern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, bool bInheritHandle, int dwProcessId); [DllImport("Kernel32.dll", EntryPoint = "QueryFullProcessImageName", SetLastError = true)] public static extern bool QueryFullProcessImageName(IntPtr hprocess, int dwFlags, StringBuilder lpExeName, out int size); [DllImport("Kernel32.dll", EntryPoint = "CloseHandle", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool CloseHandle(IntPtr hHandle);