diff --git a/DisplayMagician/DisplayMagician.csproj b/DisplayMagician/DisplayMagician.csproj index fd35243..39d817d 100644 --- a/DisplayMagician/DisplayMagician.csproj +++ b/DisplayMagician/DisplayMagician.csproj @@ -107,7 +107,6 @@ - True diff --git a/DisplayMagician/ProcessCommandLine.cs b/DisplayMagician/ProcessCommandLine.cs deleted file mode 100644 index abe5846..0000000 --- a/DisplayMagician/ProcessCommandLine.cs +++ /dev/null @@ -1,246 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics; -using System.Linq; -using System.Runtime.InteropServices; - -namespace DisplayMagician -{ - 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 Reserved3; - } - - [StructLayout(LayoutKind.Sequential)] - public struct UnicodeString - { - public ushort Length; - public ushort MaximumLength; - public IntPtr Buffer; - } - - // This is not the real struct! - // I faked it to get ProcessParameters address. - // Actual struct definition: - // https://docs.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 RtlUserProcessParameters - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public byte[] Reserved1; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] - public IntPtr[] Reserved2; - public UnicodeString ImagePathName; - public UnicodeString CommandLine; - } - - [DllImport("ntdll.dll")] - public static extern uint NtQueryInformationProcess( - 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("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 ReadStructFromProcessMemory( - IntPtr hProcess, IntPtr lpBaseAddress, out TStruct val) - { - val = default; - var structSize = Marshal.SizeOf(); - var mem = Marshal.AllocHGlobal(structSize); - try - { - if (Win32Native.ReadProcessMemory( - hProcess, lpBaseAddress, mem, (uint)structSize, out var len) && - (len == structSize)) - { - val = Marshal.PtrToStructure(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" - }[Math.Abs(error)]; - - public static int Retrieve(Process process, out string commandLine) - { - int rc = 0; - commandLine = 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(); - var memPBI = Marshal.AllocHGlobal(sizePBI); - try - { - var ret = Win32Native.NtQueryInformationProcess( - hProcess, Win32Native.PROCESS_BASIC_INFORMATION, memPBI, - (uint)sizePBI, out var len); - if (0 == ret) - { - var pbiInfo = Marshal.PtrToStructure(memPBI); - if (pbiInfo.PebBaseAddress != IntPtr.Zero) - { - if (ReadStructFromProcessMemory(hProcess, - pbiInfo.PebBaseAddress, out var pebInfo)) - { - if (ReadStructFromProcessMemory( - hProcess, pebInfo.ProcessParameters, out var ruppInfo)) - { - var clLen = ruppInfo.CommandLine.MaximumLength; - var memCL = Marshal.AllocHGlobal(clLen); - try - { - if (Win32Native.ReadProcessMemory(hProcess, - ruppInfo.CommandLine.Buffer, memCL, clLen, out len)) - { - commandLine = Marshal.PtrToStringUni(memCL); - rc = 0; - } - else - { - // couldn't read command line buffer - rc = -6; - } - } - 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 CommandLineToArgs(string commandLine) - { - if (string.IsNullOrEmpty(commandLine)) { return Array.Empty(); } - - 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); - } - } - } -}