Related
I am trying to directly access video memory for a framebuffer video device on a Raspberry Pi using C# code running via Mono. I have a C program that works fine, but when I port it to C# it consistently fails on the "map to memory" step.
The working C program (courtesy of tasanakorn) looks like this:
#include <stdio.h>
#include <syslog.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <bcm_host.h>
int process() {
DISPMANX_DISPLAY_HANDLE_T display;
DISPMANX_MODEINFO_T display_info;
DISPMANX_RESOURCE_HANDLE_T screen_resource;
VC_IMAGE_TRANSFORM_T transform;
uint32_t image_prt;
VC_RECT_T rect1;
int ret;
int fbfd = 0;
char *fbp = 0;
struct fb_var_screeninfo vinfo;
struct fb_fix_screeninfo finfo;
bcm_host_init();
display = vc_dispmanx_display_open(0);
if (!display) {
syslog(LOG_ERR, "Unable to open primary display");
return -1;
}
ret = vc_dispmanx_display_get_info(display, &display_info);
if (ret) {
syslog(LOG_ERR, "Unable to get primary display information");
return -1;
}
syslog(LOG_INFO, "Primary display is %d x %d", display_info.width, display_info.height);
fbfd = open("/dev/fb1", O_RDWR);
if (fbfd == -1) {
syslog(LOG_ERR, "Unable to open secondary display");
return -1;
}
if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo)) {
syslog(LOG_ERR, "Unable to get secondary display information");
return -1;
}
if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo)) {
syslog(LOG_ERR, "Unable to get secondary display information");
return -1;
}
syslog(LOG_INFO, "Second display is %d x %d %dbps\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel);
screen_resource = vc_dispmanx_resource_create(VC_IMAGE_RGB565, vinfo.xres, vinfo.yres, &image_prt);
if (!screen_resource) {
syslog(LOG_ERR, "Unable to create screen buffer");
close(fbfd);
vc_dispmanx_display_close(display);
return -1;
}
fbp = (char*) mmap(0, finfo.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fbfd, 0);
if (fbp <= 0) {
syslog(LOG_ERR, "Unable to create mamory mapping");
close(fbfd);
ret = vc_dispmanx_resource_delete(screen_resource);
vc_dispmanx_display_close(display);
return -1;
}
vc_dispmanx_rect_set(&rect1, 0, 0, vinfo.xres, vinfo.yres);
while (1) {
ret = vc_dispmanx_snapshot(display, screen_resource, 0);
vc_dispmanx_resource_read_data(screen_resource, &rect1, fbp, vinfo.xres * vinfo.bits_per_pixel / 8);
usleep(25 * 1000);
}
munmap(fbp, finfo.smem_len);
close(fbfd);
ret = vc_dispmanx_resource_delete(screen_resource);
vc_dispmanx_display_close(display);
}
int main(int argc, char **argv) {
setlogmask(LOG_UPTO(LOG_DEBUG));
openlog("fbcp", LOG_NDELAY | LOG_PID, LOG_USER);
return process();
}
The C# code (in which I've tried to port all of the C code related to /dev/fb1) is:
using System;
using System.Runtime.InteropServices;
namespace MainProgram {
class MainClass {
static void Main(string[] args) {
int fbfd = -1; // file descriptor for framebuffer device
int fbp = -1; // pointer to mapped framebuffer memory
uint fbs = 0; // size of mapped framebuffer memory
int result = 0; // utility result variable
try {
// Initialize (not sure if this is needed, but...).
Libc.bcm_host_init();
// Open the device. (Command line param is device, e.g. "/dev/fb0" or "/dev/fb1".)
fbfd = Libc.open(args[0], Libc.O_RDWR);
if (fbfd < 0)
throw new Exception("open: error " + Marshal.GetLastWin32Error());
Console.WriteLine("fbfd=" + fbfd);
// Get fixed screen info.
Libc.fb_fix_screeninfo fixInfo = new Libc.fb_fix_screeninfo();
result = Libc.ioctl1(fbfd, Libc.FBIOGET_FSCREENINFO, ref fixInfo);
if (result < 0)
throw new Exception("ioctl1: error " + Marshal.GetLastWin32Error());
Console.WriteLine("fbfix: mem start=" + fixInfo.smem_start.ToString("X8") + ", len=" + fixInfo.smem_len);
// Get variable screen info.
Libc.fb_var_screeninfo varInfo = new Libc.fb_var_screeninfo();
result = Libc.ioctl2(fbfd, Libc.FBIOGET_VSCREENINFO, ref varInfo);
if (result < 0)
throw new Exception("ioctl2: error " + Marshal.GetLastWin32Error());
Console.WriteLine("fbvar: res=" + varInfo.xres + "x" + varInfo.yres + ", bpp=" + varInfo.bits_per_pixel);
// Map framebuffer memory to virtual space.
fbs = fixInfo.smem_len;
Console.WriteLine("Confirm non-zero size: fbs=" + fbs);
fbp = Libc.mmap(0, fbs, Libc.PROT_READ | Libc.PROT_WRITE, Libc.MAP_SHARED, fbfd, 0);
if (fbp < 0)
throw new Exception("mmap: error " + Marshal.GetLastWin32Error());
Console.WriteLine("mmap: location=" + fbp.ToString("X8"));
}
catch (Exception ex) {
Console.WriteLine("*** Error: " + ex.Message);
}
finally {
if (fbp >= 0)
result = Libc.munmap(fbp, fbs);
if (fbfd >= 0)
result = Libc.close(fbfd);
};
}
public static class Libc {
public const int O_RDWR = 0x0002;
public const int PROT_READ = 0x04;
public const int PROT_WRITE = 0x02;
public const int MAP_FILE = 0x0001;
public const int MAP_SHARED = 0x0010;
public const int FBIOGET_VSCREENINFO = 0x4600;
public const int FBIOGET_FSCREENINFO = 0x4602;
[DllImport("libbcm_host.so", EntryPoint = "bcm_host_init")]
public static extern void bcm_host_init();
[DllImport("libc", EntryPoint="open", SetLastError = true)]
public static extern int open(
[MarshalAs(UnmanagedType.LPStr)] string filename,
[MarshalAs(UnmanagedType.I4)] int flags
);
[DllImport("libc", EntryPoint="close", SetLastError = true)]
public static extern int close(
[MarshalAs(UnmanagedType.I4)] int filedes
);
[DllImport("libc", EntryPoint="ioctl", SetLastError = true)]
public static extern int ioctl1(
[MarshalAs(UnmanagedType.I4)] int filedes,
[MarshalAs(UnmanagedType.I4)] int command,
ref fb_fix_screeninfo data
);
[DllImport("libc", EntryPoint="ioctl", SetLastError = true)]
public static extern int ioctl2(
[MarshalAs(UnmanagedType.I4)] int filedes,
[MarshalAs(UnmanagedType.I4)] int command,
ref fb_var_screeninfo data
);
[DllImport("libc", EntryPoint = "mmap", SetLastError = true)]
public static extern int mmap(
[MarshalAs(UnmanagedType.U4)] uint addr,
[MarshalAs(UnmanagedType.U4)] uint length,
[MarshalAs(UnmanagedType.I4)] int prot,
[MarshalAs(UnmanagedType.I4)] int flags,
[MarshalAs(UnmanagedType.I4)] int fdes,
[MarshalAs(UnmanagedType.I4)] int offset
);
[DllImport("libc", EntryPoint = "munmap", SetLastError = true)]
public static extern int munmap(
[MarshalAs(UnmanagedType.I4)] int addr,
[MarshalAs(UnmanagedType.U4)] uint length
);
[StructLayout(LayoutKind.Sequential)]
public struct fb_fix_screeninfo {
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public byte[] id;
[MarshalAs(UnmanagedType.U4)] public uint smem_start;
[MarshalAs(UnmanagedType.U4)] public uint smem_len;
[MarshalAs(UnmanagedType.U4)] public uint type;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 36)] public byte[] stuff;
};
[StructLayout(LayoutKind.Sequential)]
public struct fb_var_screeninfo {
[MarshalAs(UnmanagedType.U4)] public uint xres;
[MarshalAs(UnmanagedType.U4)] public uint yres;
[MarshalAs(UnmanagedType.U4)] public uint xres_virtual;
[MarshalAs(UnmanagedType.U4)] public uint yres_virtual;
[MarshalAs(UnmanagedType.U4)] public uint xoffset;
[MarshalAs(UnmanagedType.U4)] public uint yoffset;
[MarshalAs(UnmanagedType.U4)] public uint bits_per_pixel;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 132)] public byte[] stuff;
};
}
}
}
When I try to run the compiled code on the Pi, I get this following:
$ sudo mono ConsoleApp6.exe /dev/fb1
fbfd=4
fbfix: mem start=00000000, len=307200
fbvar: res=480x320, bpp=16
Confirm non-zero size: fbs=307200
*** Error: mmap: error 22
(I tried the same thing with the main monitor, also a framebuffer device, and got the following -- same error.)
$ sudo mono ConsoleApp6.exe /dev/fb0
fbfd=4
fbfix: mem start=1E876000, len=3686400
fbvar: res=1280x720, bpp=32
Confirm non-zero size: fbs=3686400
*** Error: mmap: error 22
Error 22 is EINVAL, which the mmap() documentation describes as, "We don't like addr, length, or offset (e.g., they are too large, or not aligned on a page boundary)." [It could also mean that: a) the length parameter is zero (which it isn't), or b) that both MAP_SHARED and MAP_PRIVATE are set (which they aren't)]. However, the values I'm passing in should be the same values used by the C code.
Does anyone have any idea what I'm doing wrong?
Solved. The problem is the constants PROT_READ, PROT_WRITE, MAP_SHARED, ... on the Raspberry Pi are not the same as the values I uncovered via Google searches (headslap). I found the correct values by looking at the .h files in /usr/include. Also, I discovered that a negative value for mmap is not necessarily an error; I needed to be looking specifically for MAP_FAILED (-1).
Here is the working code if anyone can use it:
using System;
using System.Runtime.InteropServices;
namespace MainProgram {
class MainClass {
static void Main(string[] args) {
int fbfd = -1; // file descriptor for framebuffer device
int fbp = -1; // pointer to mapped framebuffer memory
uint fbs = 0; // size of mapped framebuffer memory
int result = 0; // utility result variable
try {
// Initialize (not sure if this is needed, but...).
Libc.bcm_host_init();
// Open the device. (Command line param is device, e.g. "/dev/fb0" or "/dev/fb1".)
fbfd = Libc.open(args[0], Libc.O_RDWR);
if (fbfd == -1)
throw new Exception("open: result=" + fbfd + ", error=" + Marshal.GetLastWin32Error());
Console.WriteLine("fbfd=" + fbfd);
// Get fixed screen info.
Libc.fb_fix_screeninfo fixInfo = new Libc.fb_fix_screeninfo();
result = Libc.ioctl1(fbfd, Libc.FBIOGET_FSCREENINFO, ref fixInfo);
if (result == -1)
throw new Exception("ioctl1: result=" + result + ", error " + Marshal.GetLastWin32Error());
Console.WriteLine("fbfix: mem start=" + fixInfo.smem_start.ToString("X8") + ", len=" + fixInfo.smem_len);
// Get variable screen info.
Libc.fb_var_screeninfo varInfo = new Libc.fb_var_screeninfo();
result = Libc.ioctl2(fbfd, Libc.FBIOGET_VSCREENINFO, ref varInfo);
if (result == -1)
throw new Exception("ioctl2: result=" + result + ", error " + Marshal.GetLastWin32Error());
Console.WriteLine("fbvar: res=" + varInfo.xres + "x" + varInfo.yres + ", bpp=" + varInfo.bits_per_pixel);
// Map framebuffer memory to virtual space.
fbp = Libc.mmap(0, fixInfo.smem_len, Libc.PROT_READ | Libc.PROT_WRITE, Libc.MAP_SHARED, fbfd, 0);
if (fbp == Libc.MAP_FAILED)
throw new Exception("mmap: result=" + fbp + ", error " + Marshal.GetLastWin32Error());
Console.WriteLine("mmap: location=" + fbp.ToString("X8"));
}
catch (Exception ex) {
Console.WriteLine("*** Error: " + ex.Message);
}
finally {
if (fbp != -1)
result = Libc.munmap(fbp, fbs);
if (fbfd != -1)
result = Libc.close(fbfd);
};
}
public static class Libc {
public const int O_RDWR = 0x0002;
public const int PROT_READ = 0x1;
public const int PROT_WRITE = 0x2;
public const int MAP_SHARED = 0x01;
public const int MAP_FAILED = -1;
public const int FBIOGET_VSCREENINFO = 0x4600;
public const int FBIOGET_FSCREENINFO = 0x4602;
[DllImport("libbcm_host.so", EntryPoint = "bcm_host_init")]
public static extern void bcm_host_init();
[DllImport("libc", EntryPoint="open", SetLastError = true)]
public static extern int open(
[MarshalAs(UnmanagedType.LPStr)] string filename,
[MarshalAs(UnmanagedType.I4)] int flags
);
[DllImport("libc", EntryPoint="close", SetLastError = true)]
public static extern int close(
[MarshalAs(UnmanagedType.I4)] int filedes
);
[DllImport("libc", EntryPoint="ioctl", SetLastError = true)]
public static extern int ioctl1(
[MarshalAs(UnmanagedType.I4)] int filedes,
[MarshalAs(UnmanagedType.I4)] int command,
ref fb_fix_screeninfo data
);
[DllImport("libc", EntryPoint="ioctl", SetLastError = true)]
public static extern int ioctl2(
[MarshalAs(UnmanagedType.I4)] int filedes,
[MarshalAs(UnmanagedType.I4)] int command,
ref fb_var_screeninfo data
);
[DllImport("libc", EntryPoint = "mmap", SetLastError = true)]
public static extern int mmap(
[MarshalAs(UnmanagedType.U4)] uint addr,
[MarshalAs(UnmanagedType.U4)] uint length,
[MarshalAs(UnmanagedType.I4)] int prot,
[MarshalAs(UnmanagedType.I4)] int flags,
[MarshalAs(UnmanagedType.I4)] int fdes,
[MarshalAs(UnmanagedType.I4)] int offset
);
[DllImport("libc", EntryPoint = "munmap", SetLastError = true)]
public static extern int munmap(
[MarshalAs(UnmanagedType.I4)] int addr,
[MarshalAs(UnmanagedType.U4)] uint length
);
[StructLayout(LayoutKind.Sequential)]
public struct fb_fix_screeninfo {
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public byte[] id;
[MarshalAs(UnmanagedType.U4)] public uint smem_start;
[MarshalAs(UnmanagedType.U4)] public uint smem_len;
[MarshalAs(UnmanagedType.U4)] public uint type;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 36)] public byte[] stuff;
};
[StructLayout(LayoutKind.Sequential)]
public struct fb_var_screeninfo {
[MarshalAs(UnmanagedType.U4)] public uint xres;
[MarshalAs(UnmanagedType.U4)] public uint yres;
[MarshalAs(UnmanagedType.U4)] public uint xres_virtual;
[MarshalAs(UnmanagedType.U4)] public uint yres_virtual;
[MarshalAs(UnmanagedType.U4)] public uint xoffset;
[MarshalAs(UnmanagedType.U4)] public uint yoffset;
[MarshalAs(UnmanagedType.U4)] public uint bits_per_pixel;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 132)] public byte[] stuff;
};
}
}
}
I'd like to list out all CSP and CNG providers installed on a system, but I can't find a good method for doing so in C#. For CSP, I can enumerate a certain registry key (inelegant, but functional), but I've been unable to find any way to get a list of CNG providers.
Is there anything remotely like System.Security.Cryptography.Get[CSP/CNG]Providers() or similarly logical/straightforward in .NET that could be used? Thanks!
To my knowledge, there isn't anything like that in .NET Framework.
For CSP providers, enumerate the subkeys of:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\Defaults\Provider
For CNG providers, enumerate the subkeys of:
HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Cryptography\Providers
Use this to enumerate CSP providers and containers:
using Microsoft.Win32.SafeHandles;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Text;
namespace CspSample
{
struct Provider
{
public string Name { get; set; }
public int Type { get; set; }
}
class CspUtils
{
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool CryptEnumProviderTypes(
uint dwIndex,
uint pdwReserved,
uint dwFlags,
[In] ref uint pdwProvType,
StringBuilder pszTypeName,
[In] ref uint pcbTypeName);
[DllImport("Advapi32.dll")]
private static extern bool CryptEnumProviders(
int dwIndex,
IntPtr pdwReserved,
int dwFlags,
ref int pdwProvType,
StringBuilder pszProvName,
ref int pcbProvName);
public static List<Provider> ListAllProviders()
{
List<Provider> installedCSPs = new List<Provider>();
int cbName;
int dwType;
int dwIndex;
StringBuilder pszName;
dwIndex = 0;
dwType = 1;
cbName = 0;
while (CryptEnumProviders(dwIndex, IntPtr.Zero, 0, ref dwType, null, ref cbName))
{
pszName = new StringBuilder(cbName);
if (CryptEnumProviders(dwIndex++, IntPtr.Zero, 0, ref dwType, pszName, ref cbName))
{
installedCSPs.Add(new Provider { Name = pszName.ToString(), Type = dwType });
}
}
return installedCSPs;
}
const int PP_ENUMCONTAINERS = 2;
const int PROV_RSA_FULL = 1;
const int ERROR_MORE_DATA = 234;
const int ERROR_NO_MORE_ITEMS = 259;
const int CRYPT_FIRST = 1;
const int CRYPT_NEXT = 2;
//TODO: Find how to disable this flag (not machine keystore)
const int CRYPT_MACHINE_KEYSET = 0x20;
const int CRYPT_VERIFYCONTEXT = unchecked((int)0xF0000000);
public static IList<string> EnumerateKeyContainers(string providerName, int providerType)
{
ProvHandle prov;
if (!CryptAcquireContext(out prov, null, providerName, providerType, CRYPT_MACHINE_KEYSET | CRYPT_VERIFYCONTEXT))
throw new Win32Exception(Marshal.GetLastWin32Error());
List<string> list = new List<string>();
IntPtr data = IntPtr.Zero;
try
{
int flag = CRYPT_FIRST;
int len = 0;
if (!CryptGetProvParam(prov, PP_ENUMCONTAINERS, IntPtr.Zero, ref len, flag))
{
if (Marshal.GetLastWin32Error() != ERROR_MORE_DATA)
throw new Win32Exception(Marshal.GetLastWin32Error());
}
data = Marshal.AllocHGlobal(len);
do
{
if (!CryptGetProvParam(prov, PP_ENUMCONTAINERS, data, ref len, flag))
{
if (Marshal.GetLastWin32Error() == ERROR_NO_MORE_ITEMS)
break;
//throw new Win32Exception(Marshal.GetLastWin32Error());
}
list.Add(Marshal.PtrToStringAnsi(data));
flag = CRYPT_NEXT;
}
while (true);
}
finally
{
if (data != IntPtr.Zero)
{
Marshal.FreeHGlobal(data);
}
prov.Dispose();
}
return list;
}
private sealed class ProvHandle : SafeHandleZeroOrMinusOneIsInvalid
{
public ProvHandle()
: base(true)
{
}
protected override bool ReleaseHandle()
{
return CryptReleaseContext(handle, 0);
}
[DllImport("advapi32.dll")]
private static extern bool CryptReleaseContext(IntPtr hProv, int dwFlags);
}
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool CryptAcquireContext(out ProvHandle phProv, string pszContainer, string pszProvider, int dwProvType, int dwFlags);
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool CryptGetProvParam(ProvHandle hProv, int dwParam, IntPtr pbData, ref int pdwDataLen, int dwFlags);
}
}
I need to use win32 NetLocalGroupGetMembers in C#. I found and tested three solutions. All three fail with an FatalExecutionEngineError. The framework is .net 4.0
Here is a full example:
Reference to the api:
static class NetworkAPI
{
[DllImport("Netapi32.dll")]
public extern static int NetLocalGroupGetMembers([MarshalAs(UnmanagedType.LPWStr)] string servername, [MarshalAs(UnmanagedType.LPWStr)] string localgroupname, int level, out IntPtr bufptr, int prefmaxlen, out int entriesread, out int totalentries, out int resumehandle);
[DllImport("Netapi32.dll")]
public extern static int NetApiBufferFree(IntPtr Buffer);
// LOCALGROUP_MEMBERS_INFO_1 - Structure for holding members details
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct LOCALGROUP_MEMBERS_INFO_1
{
public int lgrmi1_sid;
public int lgrmi1_sidusage;
public string lgrmi1_name;
}
}
calling the function:
static void Main(string[] args)
{
int EntriesRead;
int TotalEntries;
int Resume;
IntPtr bufPtr;
string groupName = "Administrators";
NetworkAPI.NetLocalGroupGetMembers(null, groupName, 1, out bufPtr, -1, out EntriesRead, out TotalEntries, out Resume);
if (EntriesRead > 0)
{
NetworkAPI.LOCALGROUP_MEMBERS_INFO_1[] Members = new NetworkAPI.LOCALGROUP_MEMBERS_INFO_1[EntriesRead];
IntPtr iter = bufPtr;
// EntriesRead has the correct quantity of members of the group, so the group is found
for (int i = 0; i < EntriesRead; i++)
{
// --------------------------------------------------
// ==> here the FatalExecutionEngineError happens:
Members[i] = (NetworkAPI.LOCALGROUP_MEMBERS_INFO_1)Marshal.PtrToStructure(iter, typeof(NetworkAPI.LOCALGROUP_MEMBERS_INFO_1));
//
// --------------------------------------------------
iter = (IntPtr)((int)iter + Marshal.SizeOf(typeof(NetworkAPI.LOCALGROUP_MEMBERS_INFO_1)));
Console.WriteLine(Members[i].lgrmi1_name);
}
NetworkAPI.NetApiBufferFree(bufPtr);
}
}
I see the following errors:
The resume handle is a pointer. Use ref IntPtr resumehandle for that parameter, and pass IntPtr.Zero on the first call. Or if you don't need to use a resume handle declare the parameter as IntPtr resumehandle and pass IntPtr.Zero. Consult the function documentation on MSDN for the full details.
The lgrmi1_sid member of the struct is a pointer. Declare it as such: public IntPtr lgrmi1_sid.
Casting an IntPtr to an int will lead to pointer truncation on 64 bit. Either use arithmetic directly on the IntPtr, or for older C# versions cast to long. The former is better, like so: iter += Marshal.SizeOf(typeof(NetworkAPI.LOCALGROUP_MEMBERS_INFO_1));.
You do not check the return value for errors.
Fix those errors and your program will run correctly.
For the sake of completeness, here is the code how to pinvoke
NetLocalGroupGetMembers.
I corrected the code as David suggested. There is also a suggestion from Martin Liversage which I didn't implement. But it maybe usefull.
If you like it, please do not upvode this answer but upvote Davids answer, who found the errors.
Reference to the api:
public static class NetworkAPI
{
[DllImport("Netapi32.dll")]
public extern static uint NetLocalGroupGetMembers([MarshalAs(UnmanagedType.LPWStr)] string servername, [MarshalAs(UnmanagedType.LPWStr)] string localgroupname, int level, out IntPtr bufptr, int prefmaxlen, out int entriesread, out int totalentries, out IntPtr resumehandle);
[DllImport("Netapi32.dll")]
public extern static int NetApiBufferFree(IntPtr Buffer);
// LOCALGROUP_MEMBERS_INFO_1 - Structure for holding members details
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct LOCALGROUP_MEMBERS_INFO_1
{
public IntPtr lgrmi1_sid;
public int lgrmi1_sidusage;
public string lgrmi1_name;
}
// documented in MSDN
public const uint ERROR_ACCESS_DENIED = 0x0000005;
public const uint ERROR_MORE_DATA = 0x00000EA;
public const uint ERROR_NO_SUCH_ALIAS = 0x0000560;
public const uint NERR_InvalidComputer = 0x000092F;
// found by testing
public const uint NERR_GroupNotFound = 0x00008AC;
public const uint SERVER_UNAVAILABLE = 0x0006BA;
}
Calling the function:
static void Main(string[] args)
{
int EntriesRead;
int TotalEntries;
IntPtr Resume;
IntPtr bufPtr;
string groupName = "Administratoren";
string computerName = null; // null for the local machine
uint retVal = NetworkAPI.NetLocalGroupGetMembers(computerName, groupName, 1, out bufPtr, -1, out EntriesRead, out TotalEntries, out Resume);
if(retVal != 0)
{
if (retVal == NetworkAPI.ERROR_ACCESS_DENIED) { Console.WriteLine("Access denied"); return; }
if (retVal == NetworkAPI.ERROR_MORE_DATA) { Console.WriteLine("ERROR_MORE_DATA"); return; }
if (retVal == NetworkAPI.ERROR_NO_SUCH_ALIAS) { Console.WriteLine("Group not found"); return; }
if (retVal == NetworkAPI.NERR_InvalidComputer) { Console.WriteLine("Invalid computer name"); return; }
if (retVal == NetworkAPI.NERR_GroupNotFound) { Console.WriteLine("Group not found"); return; }
if (retVal == NetworkAPI.SERVER_UNAVAILABLE) { Console.WriteLine("Server unavailable"); return; }
Console.WriteLine("Unexpected NET_API_STATUS: " + retVal.ToString());
return;
}
if (EntriesRead > 0)
{
NetworkAPI.LOCALGROUP_MEMBERS_INFO_1[] Members = new NetworkAPI.LOCALGROUP_MEMBERS_INFO_1[EntriesRead];
IntPtr iter = bufPtr;
for (int i = 0; i < EntriesRead; i++)
{
Members[i] = (NetworkAPI.LOCALGROUP_MEMBERS_INFO_1)Marshal.PtrToStructure(iter, typeof(NetworkAPI.LOCALGROUP_MEMBERS_INFO_1));
//x64 safe
iter += Marshal.SizeOf(typeof(NetworkAPI.LOCALGROUP_MEMBERS_INFO_1));
Console.WriteLine(Members[i].lgrmi1_name);
}
NetworkAPI.NetApiBufferFree(bufPtr);
}
}
Using the topic Overview - Handle Enumeration, number 5, the attempt Close mutex of another process and and information from Mutex analysis, the canary in the coal mine and discovering new families of malware/, I have came up with:
Attempt 1: http://pastebin.com/QU0WBgE5
You must open Notepad first. Needless to say, this is not working for me. I need better error checking to figure out what's going on. I don't know how to get mutex pointers in the format I see them in Process Explorer.
My goal is to be able to delete/kill of the mutex handles created by a process so more than one instance can be open. I can do this manually using Process Explorer, but I want to do it programmatically.
(Based on Yahia's notes, I need more permissions.)
Attempt 2: http://pastebin.com/yyQLhesP
At least now I have some sort of error checking, most of the time DuplicateHandle returns 6 or 5, which is an invalid handle and access denied respectfully.
Working attempt (kind of):
I actually didn't require anything Yahia stated in the end. I was getting a "local" handle when I needed a remote one. Basically, what I mean is that you have to find the HandleValue using NtQuerySystemInformation and use that handle, not the one returned by OpenMutex / CreateMutex.
Granted, I can't get it to work on some applications (osk.exe -- on screen keyboard), but it worked for the application I was going for, posting code in case someone wants to take it further.
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Text;
using System.Threading;
using System.Security.AccessControl;
using System.Security.Principal;
namespace FileLockInfo
{
public class Win32API
{
[DllImport("ntdll.dll")]
public static extern int NtQueryObject(IntPtr ObjectHandle, int
ObjectInformationClass, IntPtr ObjectInformation, int ObjectInformationLength,
ref int returnLength);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern uint QueryDosDevice(string lpDeviceName, StringBuilder lpTargetPath, int ucchMax);
[DllImport("ntdll.dll")]
public static extern uint NtQuerySystemInformation(int
SystemInformationClass, IntPtr SystemInformation, int SystemInformationLength,
ref int returnLength);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr OpenMutex(UInt32 desiredAccess, bool inheritHandle, string name);
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, int dwProcessId);
[DllImport("kernel32.dll")]
public static extern int CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DuplicateHandle(IntPtr hSourceProcessHandle,
ushort hSourceHandle, IntPtr hTargetProcessHandle, out IntPtr lpTargetHandle,
uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwOptions);
[DllImport("kernel32.dll")]
public static extern IntPtr GetCurrentProcess();
public enum ObjectInformationClass : int
{
ObjectBasicInformation = 0,
ObjectNameInformation = 1,
ObjectTypeInformation = 2,
ObjectAllTypesInformation = 3,
ObjectHandleInformation = 4
}
[Flags]
public enum ProcessAccessFlags : uint
{
All = 0x001F0FFF,
Terminate = 0x00000001,
CreateThread = 0x00000002,
VMOperation = 0x00000008,
VMRead = 0x00000010,
VMWrite = 0x00000020,
DupHandle = 0x00000040,
SetInformation = 0x00000200,
QueryInformation = 0x00000400,
Synchronize = 0x00100000
}
[StructLayout(LayoutKind.Sequential)]
public struct OBJECT_BASIC_INFORMATION
{ // Information Class 0
public int Attributes;
public int GrantedAccess;
public int HandleCount;
public int PointerCount;
public int PagedPoolUsage;
public int NonPagedPoolUsage;
public int Reserved1;
public int Reserved2;
public int Reserved3;
public int NameInformationLength;
public int TypeInformationLength;
public int SecurityDescriptorLength;
public System.Runtime.InteropServices.ComTypes.FILETIME CreateTime;
}
[StructLayout(LayoutKind.Sequential)]
public struct OBJECT_TYPE_INFORMATION
{ // Information Class 2
public UNICODE_STRING Name;
public int ObjectCount;
public int HandleCount;
public int Reserved1;
public int Reserved2;
public int Reserved3;
public int Reserved4;
public int PeakObjectCount;
public int PeakHandleCount;
public int Reserved5;
public int Reserved6;
public int Reserved7;
public int Reserved8;
public int InvalidAttributes;
public GENERIC_MAPPING GenericMapping;
public int ValidAccess;
public byte Unknown;
public byte MaintainHandleDatabase;
public int PoolType;
public int PagedPoolUsage;
public int NonPagedPoolUsage;
}
[StructLayout(LayoutKind.Sequential)]
public struct OBJECT_NAME_INFORMATION
{ // Information Class 1
public UNICODE_STRING Name;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct UNICODE_STRING
{
public ushort Length;
public ushort MaximumLength;
public IntPtr Buffer;
}
[StructLayout(LayoutKind.Sequential)]
public struct GENERIC_MAPPING
{
public int GenericRead;
public int GenericWrite;
public int GenericExecute;
public int GenericAll;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SYSTEM_HANDLE_INFORMATION
{ // Information Class 16
public int ProcessID;
public byte ObjectTypeNumber;
public byte Flags; // 0x01 = PROTECT_FROM_CLOSE, 0x02 = INHERIT
public ushort Handle;
public int Object_Pointer;
public UInt32 GrantedAccess;
}
public const int MAX_PATH = 260;
public const uint STATUS_INFO_LENGTH_MISMATCH = 0xC0000004;
public const int DUPLICATE_SAME_ACCESS = 0x2;
public const int DUPLICATE_CLOSE_SOURCE = 0x1;
}
public class Win32Processes
{
const int CNST_SYSTEM_HANDLE_INFORMATION = 16;
const uint STATUS_INFO_LENGTH_MISMATCH = 0xc0000004;
public static string getObjectTypeName(Win32API.SYSTEM_HANDLE_INFORMATION shHandle, Process process)
{
IntPtr m_ipProcessHwnd = Win32API.OpenProcess(Win32API.ProcessAccessFlags.All, false, process.Id);
IntPtr ipHandle = IntPtr.Zero;
var objBasic = new Win32API.OBJECT_BASIC_INFORMATION();
IntPtr ipBasic = IntPtr.Zero;
var objObjectType = new Win32API.OBJECT_TYPE_INFORMATION();
IntPtr ipObjectType = IntPtr.Zero;
IntPtr ipObjectName = IntPtr.Zero;
string strObjectTypeName = "";
int nLength = 0;
int nReturn = 0;
IntPtr ipTemp = IntPtr.Zero;
if (!Win32API.DuplicateHandle(m_ipProcessHwnd, shHandle.Handle,
Win32API.GetCurrentProcess(), out ipHandle,
0, false, Win32API.DUPLICATE_SAME_ACCESS))
return null;
ipBasic = Marshal.AllocHGlobal(Marshal.SizeOf(objBasic));
Win32API.NtQueryObject(ipHandle, (int)Win32API.ObjectInformationClass.ObjectBasicInformation,
ipBasic, Marshal.SizeOf(objBasic), ref nLength);
objBasic = (Win32API.OBJECT_BASIC_INFORMATION)Marshal.PtrToStructure(ipBasic, objBasic.GetType());
Marshal.FreeHGlobal(ipBasic);
ipObjectType = Marshal.AllocHGlobal(objBasic.TypeInformationLength);
nLength = objBasic.TypeInformationLength;
while ((uint)(nReturn = Win32API.NtQueryObject(
ipHandle, (int)Win32API.ObjectInformationClass.ObjectTypeInformation, ipObjectType,
nLength, ref nLength)) ==
Win32API.STATUS_INFO_LENGTH_MISMATCH)
{
Marshal.FreeHGlobal(ipObjectType);
ipObjectType = Marshal.AllocHGlobal(nLength);
}
objObjectType = (Win32API.OBJECT_TYPE_INFORMATION)Marshal.PtrToStructure(ipObjectType, objObjectType.GetType());
if (Is64Bits())
{
ipTemp = new IntPtr(Convert.ToInt64(objObjectType.Name.Buffer.ToString(), 10) >> 32);
}
else
{
ipTemp = objObjectType.Name.Buffer;
}
strObjectTypeName = Marshal.PtrToStringUni(ipTemp, objObjectType.Name.Length >> 1);
Marshal.FreeHGlobal(ipObjectType);
return strObjectTypeName;
}
public static string getObjectName(Win32API.SYSTEM_HANDLE_INFORMATION shHandle, Process process)
{
IntPtr m_ipProcessHwnd = Win32API.OpenProcess(Win32API.ProcessAccessFlags.All, false, process.Id);
IntPtr ipHandle = IntPtr.Zero;
var objBasic = new Win32API.OBJECT_BASIC_INFORMATION();
IntPtr ipBasic = IntPtr.Zero;
IntPtr ipObjectType = IntPtr.Zero;
var objObjectName = new Win32API.OBJECT_NAME_INFORMATION();
IntPtr ipObjectName = IntPtr.Zero;
string strObjectName = "";
int nLength = 0;
int nReturn = 0;
IntPtr ipTemp = IntPtr.Zero;
if (!Win32API.DuplicateHandle(m_ipProcessHwnd, shHandle.Handle, Win32API.GetCurrentProcess(),
out ipHandle, 0, false, Win32API.DUPLICATE_SAME_ACCESS))
return null;
ipBasic = Marshal.AllocHGlobal(Marshal.SizeOf(objBasic));
Win32API.NtQueryObject(ipHandle, (int)Win32API.ObjectInformationClass.ObjectBasicInformation,
ipBasic, Marshal.SizeOf(objBasic), ref nLength);
objBasic = (Win32API.OBJECT_BASIC_INFORMATION)Marshal.PtrToStructure(ipBasic, objBasic.GetType());
Marshal.FreeHGlobal(ipBasic);
nLength = objBasic.NameInformationLength;
ipObjectName = Marshal.AllocHGlobal(nLength);
while ((uint)(nReturn = Win32API.NtQueryObject(
ipHandle, (int)Win32API.ObjectInformationClass.ObjectNameInformation,
ipObjectName, nLength, ref nLength))
== Win32API.STATUS_INFO_LENGTH_MISMATCH)
{
Marshal.FreeHGlobal(ipObjectName);
ipObjectName = Marshal.AllocHGlobal(nLength);
}
objObjectName = (Win32API.OBJECT_NAME_INFORMATION)Marshal.PtrToStructure(ipObjectName, objObjectName.GetType());
if (Is64Bits())
{
ipTemp = new IntPtr(Convert.ToInt64(objObjectName.Name.Buffer.ToString(), 10) >> 32);
}
else
{
ipTemp = objObjectName.Name.Buffer;
}
if (ipTemp != IntPtr.Zero)
{
byte[] baTemp2 = new byte[nLength];
try
{
Marshal.Copy(ipTemp, baTemp2, 0, nLength);
strObjectName = Marshal.PtrToStringUni(Is64Bits() ?
new IntPtr(ipTemp.ToInt64()) :
new IntPtr(ipTemp.ToInt32()));
return strObjectName;
}
catch (AccessViolationException)
{
return null;
}
finally
{
Marshal.FreeHGlobal(ipObjectName);
Win32API.CloseHandle(ipHandle);
}
}
return null;
}
public static List<Win32API.SYSTEM_HANDLE_INFORMATION>
GetHandles(Process process = null, string IN_strObjectTypeName = null, string IN_strObjectName = null)
{
uint nStatus;
int nHandleInfoSize = 0x10000;
IntPtr ipHandlePointer = Marshal.AllocHGlobal(nHandleInfoSize);
int nLength = 0;
IntPtr ipHandle = IntPtr.Zero;
while ((nStatus = Win32API.NtQuerySystemInformation(CNST_SYSTEM_HANDLE_INFORMATION, ipHandlePointer,
nHandleInfoSize, ref nLength)) ==
STATUS_INFO_LENGTH_MISMATCH)
{
nHandleInfoSize = nLength;
Marshal.FreeHGlobal(ipHandlePointer);
ipHandlePointer = Marshal.AllocHGlobal(nLength);
}
byte[] baTemp = new byte[nLength];
Marshal.Copy(ipHandlePointer, baTemp, 0, nLength);
long lHandleCount = 0;
if (Is64Bits())
{
lHandleCount = Marshal.ReadInt64(ipHandlePointer);
ipHandle = new IntPtr(ipHandlePointer.ToInt64() + 8);
}
else
{
lHandleCount = Marshal.ReadInt32(ipHandlePointer);
ipHandle = new IntPtr(ipHandlePointer.ToInt32() + 4);
}
Win32API.SYSTEM_HANDLE_INFORMATION shHandle;
List<Win32API.SYSTEM_HANDLE_INFORMATION> lstHandles = new List<Win32API.SYSTEM_HANDLE_INFORMATION>();
for (long lIndex = 0; lIndex < lHandleCount; lIndex++)
{
shHandle = new Win32API.SYSTEM_HANDLE_INFORMATION();
if (Is64Bits())
{
shHandle = (Win32API.SYSTEM_HANDLE_INFORMATION)Marshal.PtrToStructure(ipHandle, shHandle.GetType());
ipHandle = new IntPtr(ipHandle.ToInt64() + Marshal.SizeOf(shHandle) + 8);
}
else
{
ipHandle = new IntPtr(ipHandle.ToInt64() + Marshal.SizeOf(shHandle));
shHandle = (Win32API.SYSTEM_HANDLE_INFORMATION)Marshal.PtrToStructure(ipHandle, shHandle.GetType());
}
if (process != null)
{
if (shHandle.ProcessID != process.Id) continue;
}
string strObjectTypeName = "";
if (IN_strObjectTypeName != null){
strObjectTypeName = getObjectTypeName(shHandle, Process.GetProcessById(shHandle.ProcessID));
if (strObjectTypeName != IN_strObjectTypeName) continue;
}
string strObjectName = "";
if (IN_strObjectName != null){
strObjectName = getObjectName(shHandle, Process.GetProcessById(shHandle.ProcessID));
if (strObjectName != IN_strObjectName) continue;
}
string strObjectTypeName2 = getObjectTypeName(shHandle, Process.GetProcessById(shHandle.ProcessID));
string strObjectName2 = getObjectName(shHandle, Process.GetProcessById(shHandle.ProcessID));
Console.WriteLine("{0} {1} {2}", shHandle.ProcessID, strObjectTypeName2, strObjectName2);
lstHandles.Add(shHandle);
}
return lstHandles;
}
public static bool Is64Bits()
{
return Marshal.SizeOf(typeof(IntPtr)) == 8 ? true : false;
}
}
class Program
{
static void Main(string[] args)
{
String MutexName = "MSCTF.Asm.MutexDefault1";
String ProcessName = "notepad";
try
{
Process process = Process.GetProcessesByName(ProcessName)[0];
var handles = Win32Processes.GetHandles(process, "Mutant", "\\Sessions\\1\\BaseNamedObjects\\" + MutexName);
if (handles.Count == 0) throw new System.ArgumentException("NoMutex", "original");
foreach (var handle in handles)
{
IntPtr ipHandle = IntPtr.Zero;
if (!Win32API.DuplicateHandle(Process.GetProcessById(handle.ProcessID).Handle, handle.Handle, Win32API.GetCurrentProcess(), out ipHandle, 0, false, Win32API.DUPLICATE_CLOSE_SOURCE))
Console.WriteLine("DuplicateHandle() failed, error = {0}", Marshal.GetLastWin32Error());
Console.WriteLine("Mutex was killed");
}
}
catch (IndexOutOfRangeException)
{
Console.WriteLine("The process name '{0}' is not currently running", ProcessName);
}
catch (ArgumentException)
{
Console.WriteLine("The Mutex '{0}' was not found in the process '{1}'", MutexName, ProcessName);
}
Console.ReadLine();
}
}
}
You need elevated privileges for that - especially DEBUG privilege.
See:
Manipulate Privileges in Managed Code Reliably, Securely, and Efficiently (MSDN)
Debug Privilege (MSDN)
The Windows Access Control Model Part 3
Simon Mourier's answer to Stack Overflow question How to enable the SeCreateGlobalPrivilege in .NET without resorting to P/Invoke or reflection?
ObjectSecurity Methods (MSDN)
I want to read the environment variables of process B from C# code in process A. I have seen some solutions for this in C++ but haven't tried adapting these to C#.
Is this possible from C#, and if not, has anyone wrapped a C++ solution yet?
I've skimmed through the links provided by Isalamon and Daniel Hilgarth, and also the code in CLR Profiler's GetServicesEnvironment() method which seems to be doing the same thing, and after a bit of testing found that the most reliable solution is Oleksiy's code (pure C# with P/Invoke) which he published in this blog post. It still has the limitation, where you have to be a 64bit process to read the env vars of another 64bit process.
Windows solution (32 bit CLR, can access 32 and 64 bit processes),
slightly modified from https://stackoverflow.com/a/46006415 :
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
// Slightly modified from
// https://stackoverflow.com/questions/2633628/can-i-get-command-line-arguments-of-other-processes-from-net-c
// https://stackoverflow.com/a/46006415
public static class ProcessCommandLine
{
private static class Win32Native
{
public const uint PROCESS_BASIC_INFORMATION = 0;
[Flags]
public enum OpenProcessDesiredAccessFlags : uint
{
PROCESS_VM_READ = 0x0010,
PROCESS_QUERY_INFORMATION = 0x0400,
}
[StructLayout(LayoutKind.Sequential)]
public struct ProcessBasicInformation
{
public IntPtr Reserved1;
public IntPtr PebBaseAddress;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public IntPtr[] Reserved2;
public IntPtr UniqueProcessId;
public IntPtr ParentProcessId;
}
[StructLayout(LayoutKind.Sequential)]
public struct ProcessBasicInformation64
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public IntPtr[] Reserved1;
public UInt64 PebBaseAddress;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public IntPtr[] Reserved2;
public IntPtr UniqueProcessId;
public IntPtr Reserved3;
public IntPtr ParentProcessId;
public IntPtr Reserved4;
}
[StructLayout(LayoutKind.Sequential)]
public struct UnicodeString
{
public ushort Length;
public ushort MaximumLength;
public IntPtr Buffer;
}
[StructLayout(LayoutKind.Sequential)]
public struct UnicodeString64
{
public ushort Length;
public ushort MaximumLength;
public UInt32 __padding;
public UInt64 Buffer;
}
// This is not the real struct!
// I faked it to get ProcessParameters address.
// Actual struct definition:
// https://learn.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb
[StructLayout(LayoutKind.Sequential)]
public struct PEB
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public IntPtr[] Reserved;
public IntPtr ProcessParameters;
}
[StructLayout(LayoutKind.Sequential)]
public struct PEB64
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public IntPtr[] Reserved;
public UInt64 ProcessParameters;
}
/*
* https://stackoverflow.com/a/63222041
Left: Right:
32-bit struct 64-bit struct
0
ULONG MaximumLength; 0
ULONG Length; 4
ULONG Flags; 8
ULONG DebugFlags; 0c
10
PVOID ConsoleHandle; 10
ULONG ConsoleFlags; 18 +4
PVOID StdInputHandle; 20
PVOID StdOutputHandle; 28
20
PVOID StdErrorHandle; 30
UNICODE_STRING CurrentDirectoryPath; 38
PVOID CurrentDirectoryHandle; 48
30
UNICODE_STRING DllPath; 50
UNICODE_STRING ImagePathName; 60
40
UNICODE_STRING CommandLine 70
PVOID Environment; 80
ULONG StartingPositionLeft;
50
ULONG StartingPositionTop;\
ULONG Width;\
ULONG Height;\
ULONG CharWidth;\
60
ULONG CharHeight;\
ULONG ConsoleTextAttributes;\
ULONG WindowFlags;\
ULONG ShowWindowFlags;\
70
UNICODE_STRING WindowTitle; b0
UNICODE_STRING DesktopName; c0
80
UNICODE_STRING ShellInfo; d0
UNICODE_STRING RuntimeData; e0
90
RTL_DRIVE_LETTER_CURDIR DLCurrentDirectory[32];\
ULONG EnvironmentSize;\
*/
[StructLayout(LayoutKind.Sequential)]
public struct RtlUserProcessParameters
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public byte[] Reserved1;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public IntPtr[] Reserved2;
public UnicodeString ImagePathName;
public UnicodeString CommandLine;
public IntPtr Environment;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)]
public IntPtr[] Reserved3; // StartingPositionLeft, -Top, Width, Height, CharWidth, -Height, ConsoleTextAttributes, WindowFlags, ShowWindowFlags
public UnicodeString WindowTitle;
public UnicodeString DesktopName;
public UnicodeString ShellInfo;
public UnicodeString RuntimeData;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32 * 4)]
public IntPtr[] Reserved4;
public uint EnvironmentSize;
}
[StructLayout(LayoutKind.Sequential)]
public struct RtlUserProcessParameters64
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public byte[] Reserved1;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public IntPtr[] Reserved2;
public UnicodeString64 CurrentDirectoryPath;
public UnicodeString64 DllPath;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public IntPtr[] Reserved2b;
public UnicodeString64 ImagePathName;
public UnicodeString64 CommandLine;
public UInt64 Environment;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)]
public IntPtr[] Reserved3; // StartingPositionLeft, -Top, Width, Height, CharWidth, -Height, ConsoleTextAttributes, WindowFlags, ShowWindowFlags
public UnicodeString64 WindowTitle;
public UnicodeString64 DesktopName;
public UnicodeString64 ShellInfo;
public UnicodeString64 RuntimeData;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32 * 6)]
public IntPtr[] Reserved4;
public uint EnvironmentSize;
}
[DllImport("ntdll.dll")]
public static extern uint NtQueryInformationProcess(
IntPtr ProcessHandle,
uint ProcessInformationClass,
IntPtr ProcessInformation,
uint ProcessInformationLength,
out uint ReturnLength);
[DllImport("ntdll.dll")]
public static extern uint NtWow64QueryInformationProcess64(
IntPtr ProcessHandle,
uint ProcessInformationClass,
IntPtr ProcessInformation,
uint ProcessInformationLength,
out uint ReturnLength);
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(
OpenProcessDesiredAccessFlags dwDesiredAccess,
[MarshalAs(UnmanagedType.Bool)] bool bInheritHandle,
uint dwProcessId);
[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ReadProcessMemory(
IntPtr hProcess, IntPtr lpBaseAddress, IntPtr lpBuffer,
uint nSize, out uint lpNumberOfBytesRead);
[DllImport("ntdll.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool NtWow64ReadVirtualMemory64(
IntPtr hProcess, UInt64 lpBaseAddress, IntPtr lpBuffer,
UInt64 nSize, out UInt64 lpNumberOfBytesRead);
[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(IntPtr hObject);
[DllImport("shell32.dll", SetLastError = true,
CharSet = CharSet.Unicode, EntryPoint = "CommandLineToArgvW")]
public static extern IntPtr CommandLineToArgv(string lpCmdLine, out int pNumArgs);
}
private static bool ReadProcessMemory(IntPtr hProcess, UInt64 lpBaseAddress, IntPtr mem, UInt32 sz, bool force64)
{
if (!force64 && (ulong)0x100000000 > lpBaseAddress)
return Win32Native.ReadProcessMemory(hProcess, (IntPtr)lpBaseAddress, mem, sz, out UInt32 read) && read == sz;
Win32Native.NtWow64ReadVirtualMemory64(hProcess, lpBaseAddress, mem, sz, out UInt64 len64);
return len64 == sz;
}
private static bool ReadStructFromProcessMemory<TStruct>(
IntPtr hProcess, IntPtr lpBaseAddress, out TStruct val, bool bit64 = false)
{
return ReadStructFromProcessMemory<TStruct>(hProcess, (UInt64)lpBaseAddress, out val, bit64);
}
private static bool ReadStructFromProcessMemory<TStruct>(
IntPtr hProcess, UInt64 lpBaseAddress, out TStruct val, bool bit64 = false)
{
val = default;
var structSize = Marshal.SizeOf<TStruct>();
var mem = Marshal.AllocHGlobal(structSize);
try
{
bool ret = ReadProcessMemory(hProcess, lpBaseAddress, mem, (UInt32)structSize, bit64);
if (ret)
{
val = Marshal.PtrToStructure<TStruct>(mem);
return true;
}
}
finally
{
Marshal.FreeHGlobal(mem);
}
return false;
}
public static string ErrorToString(int error) =>
new string[]
{
"Success",
"Failed to open process for reading",
"Failed to query process information",
"PEB address was null",
"Failed to read PEB information",
"Failed to read process parameters",
"Failed to read command line from process",
"Failed to read environment from process",
}[Math.Abs(error)];
public static int Retrieve(Process process, out string commandLine, out List<string> environment)
{
int rc = 0;
commandLine = null;
environment = null;
var hProcess = Win32Native.OpenProcess(
Win32Native.OpenProcessDesiredAccessFlags.PROCESS_QUERY_INFORMATION |
Win32Native.OpenProcessDesiredAccessFlags.PROCESS_VM_READ, false, (uint)process.Id);
if (hProcess != IntPtr.Zero)
{
try
{
var sizePBI = Marshal.SizeOf<Win32Native.ProcessBasicInformation>();
var sizePBI64 = Marshal.SizeOf<Win32Native.ProcessBasicInformation64>();
var memPBI = Marshal.AllocHGlobal(sizePBI64);
try
{
bool bit64;
var ret = Win32Native.NtQueryInformationProcess(
hProcess, Win32Native.PROCESS_BASIC_INFORMATION, memPBI,
(uint)sizePBI, out var len);
if (0 == ret)
{
var pbiInfo64 = new Win32Native.ProcessBasicInformation64();
var pbiInfo = Marshal.PtrToStructure<Win32Native.ProcessBasicInformation>(memPBI);
if (pbiInfo.PebBaseAddress != IntPtr.Zero)
{
bit64 = false;
}
else
{
bit64 = true;
ret = Win32Native.NtWow64QueryInformationProcess64(
hProcess, Win32Native.PROCESS_BASIC_INFORMATION, memPBI,
(uint)sizePBI64, out len);
pbiInfo64 = Marshal.PtrToStructure<Win32Native.ProcessBasicInformation64>(memPBI);
}
if (pbiInfo64.PebBaseAddress != (UInt64)0)
{
Win32Native.PEB64 pebInfo64;
Win32Native.PEB pebInfo;
pebInfo.ProcessParameters = (IntPtr)0; pebInfo64.ProcessParameters = (UInt64)0;
bool ok;
if (bit64)
ok = ReadStructFromProcessMemory<Win32Native.PEB64>(hProcess, pbiInfo64.PebBaseAddress, out pebInfo64, bit64);
else
ok = ReadStructFromProcessMemory<Win32Native.PEB>(hProcess, pbiInfo.PebBaseAddress, out pebInfo, bit64);
if (ok)
{
UInt32 CommandLineSize = 0;
UInt64 CommandLineBuf = 0;
UInt32 EnvironmentSize = 0;
UInt64 EnvironmentBuf = 0;
if (bit64)
{
ok = ReadStructFromProcessMemory<Win32Native.RtlUserProcessParameters64>(hProcess, pebInfo64.ProcessParameters, out var ruppInfo64, bit64);
CommandLineSize = (UInt32)ruppInfo64.CommandLine.MaximumLength;
CommandLineBuf = ruppInfo64.CommandLine.Buffer;
EnvironmentSize = (UInt32)ruppInfo64.EnvironmentSize;
EnvironmentBuf = ruppInfo64.Environment;
}
else
{
ok = ReadStructFromProcessMemory<Win32Native.RtlUserProcessParameters>(hProcess, (UInt64)pebInfo.ProcessParameters, out var ruppInfo, bit64);
CommandLineSize = ruppInfo.CommandLine.MaximumLength;
CommandLineBuf = (UInt64)ruppInfo.CommandLine.Buffer;
EnvironmentSize = ruppInfo.EnvironmentSize;
EnvironmentBuf = (UInt64)ruppInfo.Environment;
}
if (ok)
{
var memCL = Marshal.AllocHGlobal((IntPtr)CommandLineSize);
try
{
if (ReadProcessMemory(hProcess, CommandLineBuf, memCL, CommandLineSize, bit64))
{
commandLine = Marshal.PtrToStringUni(memCL);
rc = 0;
}
else
{
// couldn't read command line buffer
rc = -6;
}
}
finally
{
Marshal.FreeHGlobal(memCL);
}
memCL = Marshal.AllocHGlobal((IntPtr)EnvironmentSize);
try
{
if (ReadProcessMemory(hProcess, EnvironmentBuf, memCL, EnvironmentSize, bit64))
{
environment = new List<string>(40);
IntPtr readPtr = memCL;
while (EnvironmentSize > 0 && Marshal.ReadInt16(readPtr) != 0)
{
string slice = Marshal.PtrToStringUni(readPtr);
environment.Add(slice);
uint size = 0;
do
{
size += 2;
readPtr += 2;
}
while (Marshal.ReadInt16(readPtr) != 0);
size += 2;
readPtr += 2;
EnvironmentSize -= size;
}
rc = 0;
}
else
{
// couldn't read command line buffer
rc = -7;
}
}
finally
{
Marshal.FreeHGlobal(memCL);
}
}
else
{
// couldn't read ProcessParameters
rc = -5;
}
}
else
{
// couldn't read PEB information
rc = -4;
}
}
else
{
// PebBaseAddress is null
rc = -3;
}
}
else
{
// NtQueryInformationProcess failed
rc = -2;
}
}
finally
{
Marshal.FreeHGlobal(memPBI);
}
}
finally
{
Win32Native.CloseHandle(hProcess);
}
}
else
{
// couldn't open process for VM read
rc = -1;
}
return rc;
}
public static IReadOnlyList<string> CommandLineToArgs(string commandLine)
{
if (string.IsNullOrEmpty(commandLine)) { return new string[] { }; }
var argv = Win32Native.CommandLineToArgv(commandLine, out var argc);
if (argv == IntPtr.Zero)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
try
{
var args = new string[argc];
for (var i = 0; i < args.Length; ++i)
{
var p = Marshal.ReadIntPtr(argv, i * IntPtr.Size);
args[i] = Marshal.PtrToStringUni(p);
}
return args.ToList().AsReadOnly();
}
finally
{
Marshal.FreeHGlobal(argv);
}
}
}
Linux solution (32 and 64 bit can be freely mixed):
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
public static class ProcessCommandLine
{
public static int Retrieve(Process process, out string commandLine, out List<string> environment)
{
int pid = process.Id;
try
{
commandLine = File.ReadAllText("/proc/" + pid + "/cmdline").Replace('\0', ' ');
environment = File.ReadAllText("/proc/" + pid + "/environ").Split('\0').ToList();
return 0;
}
catch (Exception e)
{
commandLine = null; environment = null;
Console.WriteLine("ProcessCommandLine: Cannot read file - maybe you have to 'sudo mount -t procfs none /proc'? Maybe you have insufficient rights?");
Console.WriteLine("ProcessCommandLine: Exception was: " + e.GetType() + ": " + e.Message);
}
return -11;
}
}