I was recently running into Access is Denied errors (error code 5 in my case), while running the Win32 OpenProcess API and then later while running CreateProcessAsUser. In my case, I was running on Windows 10, but I suspect it's similar, but since I got it working I thought I would share a couple things that helped me.
As I was using C# my Win32 method signature is as follows:
[DllImport("kernel32.dll")]
private static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId);
One key factor that effected the success of accessing the existing process, which in my case was a winlogon.exe process, was to properly defined the right "desired access" value. In my case, I used a constant "MAXIMUM_ALLOWED" defined as:
private const uint MAXIMUM_ALLOWED = 0x2000000;
This call to the service looks like this:
IntPtr hProcess = OpenProcess(MAXIMUM_ALLOWED, false, targetWinlogonProcessId);
This established the right kind of access. I was also running my process (web service) as the LocalSystem account, which had pretty good privileges. It started off as:
Please note, I was able to run this command using the SYSTEM account, by downloading PsExec.exe and running PsExec.exe -i -s cmd.exe to launch a command prompt so I could query the privileges using that account. You can find a good list of permissions here:
https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/user-rights-assignment
In my case, I wanted to add SeAssignPrimaryTokenPrivilege and SeIncreaseQuotaPrivilege, which I added via secpol.msc:
Your particular permissions required may depend on the account you're using, but I hope this helps!