C# using SendMessage, problem with WM_COPYDATA - c#

I've been spending a few days (or more) trying to get this to work.
The application at hand is FTPRush, and I know there is a cmd line application called rush_cmdline.exe which uses SendMessage to send requests to FTPRush.
From debugging the rush_cmdline.exe I can see lParam, wParam, Message and hWnd.
My code is as follows (using SendMessage, not SendMessageW):
[DllImport("User32.dll", EntryPoint = "FindWindow")]
public static extern Int32 FindWindow(String lpClassName, String lpWindowName);
[DllImport("USER32.DLL", EntryPoint= "SendMessage")]
public static extern IntPtr SendMessage(int hWnd, int Msg, int wParam, IntPtr lParam);
And I've tried a another specification also:
[DllImport("User32.dll", EntryPoint = "SendMessage")]
public static extern int SendMessage(int hWnd, int Msg, int wParam, ref COPYDATASTRUCT lParam);
The handle (hWnd) is not the problem, as this works:
int ftprush = FindWindow("TfmRush", null);
ShowWindow(ftprush, 8);
Which (I didn't paste the dllimport as it's not important here. Let me know if you wish to see it) brings the window to front. Also, I checked by debugging rush_cmdline.exe. So the handle is the same.
Two attempts which both fail (silently):
public const Int32 WM_COPYDATA = 0x4A;
string msg = "RushApp.FTP.Login('backup','',0); ";
// 1
byte[] array = Encoding.UTF8.GetBytes((string)msg);
int size = Marshal.SizeOf(array[0]) * array.Length + Marshal.SizeOf(array[0]);
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.Copy(array, 0, ptr, array.Length);
Marshal.WriteByte(ptr, size - 1, 0);
SendMessage(ftprush, WM_COPYDATA, 0, ptr);
// 2
public struct COPYDATASTRUCT
{
public IntPtr dwData;
public int cbData;
[MarshalAs(UnmanagedType.LPStr)]
public string lpData;
}
COPYDATASTRUCT cds;
cds.dwData = (IntPtr)100;
cds.lpData = msg;
cds.cbData = sarr.Length + 1;
SendMessage(ftprush, WM_COPYDATA, 0, ref cds);
I would expect at least the 2nd solution to work, as it matches up pretty well with this: perl example
Any enlightenment is GREATLY appreciated!
Thanks,
Frank
UPDATE:
string msg = "RushApp.FTP.Login('backup','',0);\0";
var cds = new COPYDATASTRUCT
{
dwData = new IntPtr(3),
cbData = msg.Length + 1,
lpData = msg
};
IntPtr ftprush = FindWindow("TfmRush", null);
SendMessage(ftprush, WM_COPYDATA, IntPtr.Zero, ref cds);

My Definitions have
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, ref COPYDATASTRUCT lParam);
public struct COPYDATASTRUCT {
public int cbData;
public IntPtr dwData;
[MarshalAs(UnmanagedType.LPStr)] public string lpData;
}
var cds = new Win32.COPYDATASTRUCT {
dwData = new IntPtr(3),
cbData = str.Length + 1,
lpData = str
};
Win32.SendMessage(ftprush, Win32.WM_COPYDATA, IntPtr.Zero, ref cds);
Of course, make sure that str is null terminated "\0"
Alternatively a definition given by PInvoke.NET is
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, StringBuilder lParam);
//If you use '[Out] StringBuilder', initialize the string builder with proper length first.

Between the 2 answers above I cobbled together a working example. Bryce Wagner's class works, so I added a method to use SendMessageTimeout to send the data. it's a static method, so you just call it to send data. This isn't really my work, just gluing together and sharing back.
[StructLayout(LayoutKind.Sequential)]
public struct CopyData: IDisposable {
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
static extern IntPtr SendMessageTimeout(IntPtr hWnd, uint Msg, IntPtr wParam, ref CopyData target,
SendMessageTimeoutFlags fuFlags, uint uTimeout, out UIntPtr lpdwResult);
[Flags]
enum SendMessageTimeoutFlags: uint {
SMTO_NORMAL = 0x0,
SMTO_BLOCK = 0x1,
SMTO_ABORTIFHUNG = 0x2,
SMTO_NOTIMEOUTIFNOTHUNG = 0x8
}
const uint WM_COPYDATA = 0x4A;
public IntPtr dwData;
public int cbData;
public IntPtr lpData;
public void Dispose() {
if (lpData != IntPtr.Zero) {
Marshal.FreeCoTaskMem(lpData);
lpData = IntPtr.Zero;
cbData = 0;
}
}
public string AsAnsiString {
get { return Marshal.PtrToStringAnsi(lpData, cbData); }
}
public string AsUnicodeString {
get { return Marshal.PtrToStringUni(lpData); }
}
public static CopyData CreateForString(int dwData, string value, bool Unicode = false) {
var result = new CopyData();
result.dwData = (IntPtr) dwData;
result.lpData = Unicode ? Marshal.StringToCoTaskMemUni(value) : Marshal.StringToCoTaskMemAnsi(value);
result.cbData = value.Length + 1;
return result;
}
public static UIntPtr Send(IntPtr targetHandle, int dwData, string value, uint timeoutMs = 1000, bool Unicode = false) {
var cds = CopyData.CreateForString(dwData, value, Unicode);
UIntPtr result;
SendMessageTimeout(targetHandle, WM_COPYDATA, IntPtr.Zero, ref cds, SendMessageTimeoutFlags.SMTO_NORMAL, timeoutMs, out result);
cds.Dispose();
return result;
}
}
To use it:
CopyData.Send(targetHandle, 1234, "This is a test");
That uses the default 1 second timeout.

The order of arguments in the COPYDATASTRUCT are critically important, and Bob Vale's answer has them in the wrong order. http://msdn.microsoft.com/en-us/library/windows/desktop/ms649010(v=vs.85).aspx It should be in this order:
[StructLayout(LayoutKind.Sequential)]
public struct COPYDATASTRUCT
{
public IntPtr dwData;
public int cbData;
public IntPtr lpData;
}
I haven't gotten the MarshalAs(UnmanagedType.LPStr)] public string lpData to work either. I've only gotten it to work by doing the marshalling myself:
[StructLayout(LayoutKind.Sequential)]
public struct COPYDATASTRUCT : IDisposable
{
public IntPtr dwData;
public int cbData;
public IntPtr lpData;
/// <summary>
/// Only dispose COPYDATASTRUCT if you were the one who allocated it
/// </summary>
public void Dispose()
{
if (lpData != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(lpData);
lpData = IntPtr.Zero;
cbData = 0;
}
}
public string AsAnsiString { get { return Marshal.PtrToStringAnsi(lpData, cbData); } }
public string AsUnicodeString { get { return Marshal.PtrToStringUni(lpData); } }
public static COPYDATASTRUCT CreateForString(int dwData, string value, bool Unicode = false)
{
var result = new COPYDATASTRUCT();
result.dwData = (IntPtr)dwData;
result.lpData = Unicode ? Marshal.StringToCoTaskMemUni(value) : Marshal.StringToCoTaskMemAnsi(value);
result.cbData = value.Length + 1;
return result;
}
}

Related

Failing when using Pinvoke calling UpdateProcThreadAttribute for AppContainer security capabilities

I am trying to launch an appcontainer using C# and pinvoke and getting hung up setting the security capabilities into the attribute list via UpdateProcThreadAttribute().
The GetLastError() returns
87 - invalid parameter
Similar c++ code from this article (github link at the bottom https://scorpiosoftware.net/2019/01/15/fun-with-appcontainers/) works well.
Code: (pinvoke structs/enums/externs below)
public bool testAppContainer()
{
const uint SE_GROUP_ENABLED = 0x00000004;
const int ProcThreadAttributeSecurityCapabilities = 0x00020009;
STARTUPINFOEX startupInfoEx = new STARTUPINFOEX();
PROCESSINFO pInfo = new PROCESSINFO();
SECURITY_CAPABILITIES sc = new SECURITY_CAPABILITIES();
IntPtr pSidInternetClientServer = IntPtr.Zero;
startupInfoEx.StartupInfo.cb = Marshal.SizeOf(startupInfoEx);
string appContainerName = "Test.AppContainer";
try
{
//get the size, then the sid
IntPtr clientServerSid = IntPtr.Zero; uint cbSid = 0;
SecurityNative.CreateWellKnownSid(SecurityNative.WELL_KNOWN_SID_TYPE.WinCapabilityInternetClientServerSid, clientServerSid, pSidInternetClientServer, ref cbSid);
pSidInternetClientServer = Marshal.AllocCoTaskMem(Convert.ToInt32(cbSid));
if (!CreateWellKnownSid(WELL_KNOWN_SID_TYPE.WinCapabilityInternetClientServerSid, clientServerSid, pSidInternetClientServer, ref cbSid))
{
throw new ApplicationException($"Unable to create well known Client Server capability sid!");
}
//create an array of one capability
SID_AND_ATTRIBUTES[] sidAndAttributes = new SID_AND_ATTRIBUTES[1];
sidAndAttributes[0].Sid = pSidInternetClientServer;
sidAndAttributes[0].Attributes = SE_GROUP_ENABLED;
sc.Capabilities = sidAndAttributes;
sc.CapabilityCount = 1;
IntPtr appConSid = IntPtr.Zero;
int hResult = AppContainerNative.DeriveAppContainerSidFromAppContainerName(appContainerName, out appConSid);
if (hResult < 0)
{
throw new ApplicationException($"Application container {appContainerName} was not found on the machine (err code {hResult})");
}
//security capabilities for existing app container
sc.AppContainerSid = appConSid;
const int reserved = 0; var size = IntPtr.Zero; int attributeCount = 1;
bool alreadyInit = ProcessNative.InitializeProcThreadAttributeList(IntPtr.Zero, attributeCount, reserved, ref size);
if (alreadyInit || size == IntPtr.Zero)
{
throw new Exception(string.Format("Couldn't get the size of the attribute list for {0} attributes", attributeCount));
}
startupInfoEx.lpAttributeList = Marshal.AllocHGlobal(size);
if (startupInfoEx.lpAttributeList == IntPtr.Zero)
{
throw new Exception("Couldn't reserve space for a new attribute list");
}
bool initAttributeList = ProcessNative.InitializeProcThreadAttributeList(startupInfoEx.lpAttributeList, attributeCount, reserved, ref size);
if (!initAttributeList )
{
throw new Exception("Couldn't create new attribute list");
}
//also tried this (pass in unmanagedAddr instead of ref sc)
// IntPtr unmanagedAddr = Marshal.AllocHGlobal(Marshal.SizeOf(sc));
// Marshal.StructureToPtr(sc, unmanagedAddr, true);
bool success = UpdateProcThreadAttribute(startupInfoEx.lpAttributeList, reserved, (IntPtr)ProcThreadAttributeSecurityCapabilities,
ref sc, (IntPtr)Marshal.SizeOf(sc), IntPtr.Zero, IntPtr.Zero);
// Marshal.FreeHGlobal(unmanagedAddr);
// unmanagedAddr = IntPtr.Zero;
if (!success)
{
throw new Exception($"Error adding security capabilities to process launch. Error Code: {Marshal.GetLastWin32Error()}");
}
var pSec = new SECURITY_ATTRIBUTES(); pSec.nLength = Marshal.SizeOf(pSec);
var tSec = new SECURITY_ATTRIBUTES(); tSec.nLength = Marshal.SizeOf(tSec);
success = CreateProcess(null, #"c:\windows\notepad.exe", ref pSec, ref tSec, false,
(uint)ProcessNative.CreateProcessFlags.EXTENDED_STARTUPINFO_PRESENT, IntPtr.Zero, null, ref startupInfoEx, out pInfo);
if (success)
{
System.Diagnostics.Debug.WriteLine($"Created new app container process {pInfo.dwProcessId}!");
return true;
}
}
finally
{
// Free the attribute list
if (startupInfoEx.lpAttributeList != IntPtr.Zero)
{
ProcessNative.DeleteProcThreadAttributeList(startupInfoEx.lpAttributeList);
Marshal.FreeHGlobal(startupInfoEx.lpAttributeList);
}
// Close process and thread handles
if (pInfo.hProcess != IntPtr.Zero)
{
ProcessNative.CloseHandle(pInfo.hProcess);
}
if (pInfo.hThread != IntPtr.Zero)
{
ProcessNative.CloseHandle(pInfo.hThread);
}
Marshal.FreeHGlobal(startupInfoEx.lpAttributeList);
if (pSidInternetClientServer != IntPtr.Zero)
Marshal.FreeCoTaskMem(pSidInternetClientServer);
}
return true;
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_CAPABILITIES
{
public IntPtr AppContainerSid;
[MarshalAs(UnmanagedType.ByValArray)]
public SID_AND_ATTRIBUTES[] Capabilities;
public uint CapabilityCount;
public uint Reserved;
}
[StructLayout(LayoutKind.Sequential)]
public struct SID_AND_ATTRIBUTES
{
public IntPtr Sid;
public UInt32 Attributes;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct PROCESSINFO
{
public IntPtr hProcess;
public IntPtr hThread;
public Int32 dwProcessId;
public Int32 dwThreadId;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct STARTUPINFO
{
public Int32 cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public Int32 dwX;
public Int32 dwY;
public Int32 dwXSize;
public Int32 dwYSize;
public Int32 dwXCountChars;
public Int32 dwYCountChars;
public Int32 dwFillAttribute;
public Int32 dwFlags;
public Int16 wShowWindow;
public Int16 cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int nLength;
public IntPtr lpSecurityDescriptor;
public int bInheritHandle;
}
public enum WELL_KNOWN_SID_TYPE
{
WinNullSid,
WinWorldSid,
WinLocalSid,
WinCreatorOwnerSid,
WinCreatorGroupSid,
WinCreatorOwnerServerSid,
WinCreatorGroupServerSid,
WinNtAuthoritySid,
WinDialupSid,
WinNetworkSid,
WinBatchSid,
WinInteractiveSid,
WinServiceSid,
WinAnonymousSid,
WinProxySid,
WinEnterpriseControllersSid,
WinSelfSid,
WinAuthenticatedUserSid,
WinRestrictedCodeSid,
WinTerminalServerSid,
WinRemoteLogonIdSid,
WinLogonIdsSid,
WinLocalSystemSid,
WinLocalServiceSid,
WinNetworkServiceSid,
WinBuiltinDomainSid,
WinBuiltinAdministratorsSid,
WinBuiltinUsersSid,
WinBuiltinGuestsSid,
WinBuiltinPowerUsersSid,
WinBuiltinAccountOperatorsSid,
WinBuiltinSystemOperatorsSid,
WinBuiltinPrintOperatorsSid,
WinBuiltinBackupOperatorsSid,
WinBuiltinReplicatorSid,
WinBuiltinPreWindows2000CompatibleAccessSid,
WinBuiltinRemoteDesktopUsersSid,
WinBuiltinNetworkConfigurationOperatorsSid,
WinAccountAdministratorSid,
WinAccountGuestSid,
WinAccountKrbtgtSid,
WinAccountDomainAdminsSid,
WinAccountDomainUsersSid,
WinAccountDomainGuestsSid,
WinAccountComputersSid,
WinAccountControllersSid,
WinAccountCertAdminsSid,
WinAccountSchemaAdminsSid,
WinAccountEnterpriseAdminsSid,
WinAccountPolicyAdminsSid,
WinAccountRasAndIasServersSid,
WinNTLMAuthenticationSid,
WinDigestAuthenticationSid,
WinSChannelAuthenticationSid,
WinThisOrganizationSid,
WinOtherOrganizationSid,
WinBuiltinIncomingForestTrustBuildersSid,
WinBuiltinPerfMonitoringUsersSid,
WinBuiltinPerfLoggingUsersSid,
WinBuiltinAuthorizationAccessSid,
WinBuiltinTerminalServerLicenseServersSid,
WinBuiltinDCOMUsersSid,
WinBuiltinIUsersSid,
WinIUserSid,
WinBuiltinCryptoOperatorsSid,
WinUntrustedLabelSid,
WinLowLabelSid,
WinMediumLabelSid,
WinHighLabelSid,
WinSystemLabelSid,
WinWriteRestrictedCodeSid,
WinCreatorOwnerRightsSid,
WinCacheablePrincipalsGroupSid,
WinNonCacheablePrincipalsGroupSid,
WinEnterpriseReadonlyControllersSid,
WinAccountReadonlyControllersSid,
WinBuiltinEventLogReadersGroup,
WinNewEnterpriseReadonlyControllersSid,
WinBuiltinCertSvcDComAccessGroup,
WinMediumPlusLabelSid,
WinLocalLogonSid,
WinConsoleLogonSid,
WinThisOrganizationCertificateSid,
WinApplicationPackageAuthoritySid,
WinBuiltinAnyPackageSid,
WinCapabilityInternetClientSid,
WinCapabilityInternetClientServerSid,
WinCapabilityPrivateNetworkClientServerSid,
WinCapabilityPicturesLibrarySid,
WinCapabilityVideosLibrarySid,
WinCapabilityMusicLibrarySid,
WinCapabilityDocumentsLibrarySid,
WinCapabilitySharedUserCertificatesSid,
WinCapabilityEnterpriseAuthenticationSid,
WinCapabilityRemovableStorageSid,
WinBuiltinRDSRemoteAccessServersSid,
WinBuiltinRDSEndpointServersSid,
WinBuiltinRDSManagementServersSid,
WinUserModeDriversSid,
WinBuiltinHyperVAdminsSid,
WinAccountCloneableControllersSid,
WinBuiltinAccessControlAssistanceOperatorsSid,
WinBuiltinRemoteManagementUsersSid,
WinAuthenticationAuthorityAssertedSid,
WinAuthenticationServiceAssertedSid,
WinLocalAccountSid,
WinLocalAccountAndAdministratorSid,
WinAccountProtectedUsersSid,
WinCapabilityAppointmentsSid,
WinCapabilityContactsSid,
WinAccountDefaultSystemManagedSid,
WinBuiltinDefaultSystemManagedGroupSid,
WinBuiltinStorageReplicaAdminsSid,
WinAccountKeyAdminsSid,
WinAccountEnterpriseKeyAdminsSid,
WinAuthenticationKeyTrustSid,
WinAuthenticationKeyPropertyMFASid,
WinAuthenticationKeyPropertyAttestationSid,
WinAuthenticationFreshKeyAuthSid,
WinBuiltinDeviceOwnersSid
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct STARTUPINFOEX
{
public STARTUPINFO StartupInfo;
public IntPtr lpAttributeList;
}
[DllImport("advapi32.dll", SetLastError = true)]
internal static extern bool CreateWellKnownSid(WELL_KNOWN_SID_TYPE WellKnownSidType, IntPtr DomainSid, IntPtr pSid, ref uint cbSid);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool CreateProcess(string lpApplicationName, string lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes,
ref SECURITY_ATTRIBUTES lpThreadAttributes,
bool bInheritHandles, uint dwCreationFlags, IntPtr lpEnvironment,
string lpCurrentDirectory, [In] ref STARTUPINFOEX lpStartupInfo, out PROCESSINFO lpProcessInformation);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool InitializeProcThreadAttributeList(IntPtr lpAttributeList, int dwAttributeCount, int dwFlags, ref IntPtr lpSize);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool UpdateProcThreadAttribute(IntPtr lpAttributeList, uint dwFlags, IntPtr Attribute, [MarshalAs(UnmanagedType.Struct), In] ref SECURITY_CAPABILITIES caps, IntPtr cbSize, IntPtr lpPreviousValue, IntPtr lpReturnSize);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern void DeleteProcThreadAttributeList(IntPtr lpAttributeList);
Wow, first off, this is some great work and I'm curious how you are leveraging it (CICD? Security?). As a challenge I played around with the code and noticed that your declaration for the SECURITY_CAPABILITIES structure wasn't matching with the Microsoft documentation. They list the Capabilities member as a pointer to the SID_AND_ATTRIBUTES structure, not a structure itself. So, I changed that to IntPtr making it...
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_CAPABILITIES
{
public IntPtr AppContainerSid;
public IntPtr Capabilities;
public uint CapabilityCount;
public uint Reserved;
}
I'm not sure how to marshal an array of structs to an IntPtr so for now my example below is the single capability you listed.
//create one capability
SID_AND_ATTRIBUTES sidAndAttributes = new SID_AND_ATTRIBUTES();
sidAndAttributes.Sid = pSidInternetClientServer;
sidAndAttributes.Attributes = SE_GROUP_ENABLED;
IntPtr sidAndAttributesPtr = Marshal.AllocHGlobal(Marshal.SizeOf(sidAndAttributes));
Marshal.StructureToPtr(sidAndAttributes, sidAndAttributesPtr, false);
sc.Capabilities = sidAndAttributesPtr;
sc.CapabilityCount = 1;
This allowed notepad.exe to launch with AppContainer integrity and containing the capability flag you specified.
Thank you for the reply! I did get this to work. Your suggestion did help. This does actually launch a notepad.exe in an app container with two capabilities. Here is the code for the marshal for all that want to try this. My objective is to containerize some programs that we are using to protect the servers as much as possible.
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_CAPABILITIES
{
public IntPtr AppContainerSid;
public IntPtr Capabilities;
public int CapabilityCount;
public int Reserved;
}
private void AddDefaultCapabilitiesSid(ref SECURITY_CAPABILITIES sc)
{
//get the size, then the sid
IntPtr mem = IntPtr.Zero;
IntPtr clientServerSid = IntPtr.Zero;
IntPtr privateNetworkSid = IntPtr.Zero;
IntPtr pSid = IntPtr.Zero;
uint cbSid = 0;
//get the size, then the sid
SecurityNative.CreateWellKnownSid(SecurityNative.WELL_KNOWN_SID_TYPE.WinCapabilityInternetClientServerSid, pSid, clientServerSid, ref cbSid);
clientServerSid = Marshal.AllocCoTaskMem(Convert.ToInt32(cbSid));
if (!SecurityNative.CreateWellKnownSid(SecurityNative.WELL_KNOWN_SID_TYPE.WinCapabilityInternetClientServerSid, pSid, clientServerSid, ref cbSid))
{
throw new ApplicationException($"Unable to create well known Client Server capability sid!");
}
//get the size, then the sid
SecurityNative.CreateWellKnownSid(SecurityNative.WELL_KNOWN_SID_TYPE.WinCapabilityPrivateNetworkClientServerSid, pSid, privateNetworkSid, ref cbSid);
privateNetworkSid = Marshal.AllocCoTaskMem(Convert.ToInt32(cbSid));
if (!SecurityNative.CreateWellKnownSid(SecurityNative.WELL_KNOWN_SID_TYPE.WinCapabilityPrivateNetworkClientServerSid, pSid, privateNetworkSid, ref cbSid))
{
throw new ApplicationException($"Unable to create well known Client Server capability sid!");
}
SecurityNative.SID_AND_ATTRIBUTES[] sidAndAttributes = new SecurityNative.SID_AND_ATTRIBUTES[2];
sidAndAttributes[0].Sid = clientServerSid;
sidAndAttributes[0].Attributes = SE_GROUP_ENABLED;
sidAndAttributes[1].Sid = privateNetworkSid;
sidAndAttributes[1].Attributes = SE_GROUP_ENABLED;
int arraySize = sidAndAttributes.Length;
int elemSize = Marshal.SizeOf(typeof(SecurityNative.SID_AND_ATTRIBUTES));
IntPtr result = Marshal.AllocHGlobal(elemSize * arraySize);
mem = new IntPtr(result.ToInt64());
for (int i = 0; i < arraySize; i++)
{
IntPtr ptr = new IntPtr(result.ToInt64() + elemSize * i);
Marshal.StructureToPtr(sidAndAttributes[i], ptr, false);
MemToFree.Add(ptr); //free this mem later
}
sc.Capabilities = mem;
sc.CapabilityCount = arraySize;
}

Standard output from CreateProcessAsUser Always Empty in C#

The first Call to Launch Method Write Into file echo1, but I can't capture the standard output of the second line of the test, what I am doing wrong?
[TestMethod]
public void Launch()
{
var resp = ProcessAsUserTest.ProcessAsUser.Launch("cmd.exe /c \"echo 1\" >> c:\\temp\\echo1");
var resp1 = ProcessAsUserTest.ProcessAsUser.Launch("cmd.exe /c \"echo 1\"");
Assert.AreEqual("1", resp1);
}
I was follow this thread but It has not answer: Capture standard output from CreateProcessAsUser in C#
I get the implementation from https://social.msdn.microsoft.com/Forums/vstudio/en-US/0c0ca087-5e7b-4046-93cb-c7b3e48d0dfb/how-run-client-application-as-a-windows-service-in-c?forum=csharpgeneral
and I've edited the code to get standard output same as the first link without answer.
Here the implementation:
using Microsoft.Win32.SafeHandles;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace ProcessAsUserTest
{
[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public uint dwProcessId;
public uint dwThreadId;
}
[StructLayout(LayoutKind.Sequential)]
internal struct SECURITY_ATTRIBUTES
{
public uint nLength;
public IntPtr lpSecurityDescriptor;
public bool bInheritHandle;
}
[StructLayout(LayoutKind.Sequential)]
public struct STARTUPINFO
{
public uint cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public uint dwX;
public uint dwY;
public uint dwXSize;
public uint dwYSize;
public uint dwXCountChars;
public uint dwYCountChars;
public uint dwFillAttribute;
public uint dwFlags;
public short wShowWindow;
public short cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
internal enum SECURITY_IMPERSONATION_LEVEL
{
SecurityAnonymous,
SecurityIdentification,
SecurityImpersonation,
SecurityDelegation
}
internal enum TOKEN_TYPE
{
TokenPrimary = 1,
TokenImpersonation
}
[Flags]
enum HANDLE_FLAGS
{
INHERIT = 1,
}
public class ProcessAsUser
{
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool CreateProcessAsUser(
IntPtr hToken,
string lpApplicationName,
string lpCommandLine,
ref SECURITY_ATTRIBUTES lpProcessAttributes,
ref SECURITY_ATTRIBUTES lpThreadAttributes,
bool bInheritHandles,
uint dwCreationFlags,
IntPtr lpEnvironment,
string lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
[DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx", SetLastError = true)]
private static extern bool DuplicateTokenEx(
IntPtr hExistingToken,
uint dwDesiredAccess,
ref SECURITY_ATTRIBUTES lpThreadAttributes,
Int32 ImpersonationLevel,
Int32 dwTokenType,
ref IntPtr phNewToken);
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool OpenProcessToken(
IntPtr ProcessHandle,
UInt32 DesiredAccess,
ref IntPtr TokenHandle);
[DllImport("userenv.dll", SetLastError = true)]
private static extern bool CreateEnvironmentBlock(
ref IntPtr lpEnvironment,
IntPtr hToken,
bool bInherit);
[DllImport("userenv.dll", SetLastError = true)]
private static extern bool DestroyEnvironmentBlock(
IntPtr lpEnvironment);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CloseHandle(
IntPtr hObject);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CreateNamedPipe(string name, int openMode, int pipeMode, int maxInstances, int outBufSize, int inBufSize, int timeout, IntPtr lpPipeAttributes);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr CreateFile(string lpFileName, int dwDesiredAccess, int dwShareMode, IntPtr lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, HandleRef hTemplateFile);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
private static extern IntPtr GetStdHandle(int whichHandle);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool GetExitCodeProcess(IntPtr process, ref UInt32 exitCode);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetHandleInformation(IntPtr hObject, HANDLE_FLAGS dwMask, HANDLE_FLAGS dwFlags);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int GetConsoleOutputCP();
[DllImport("kernel32.dll")]
static extern int WaitForSingleObject(IntPtr hHandle, int dwMilliseconds);
private const short SW_SHOW = 5;
private const short SW_HIDE = 0;
private const uint TOKEN_QUERY = 0x0008;
private const uint TOKEN_DUPLICATE = 0x0002;
private const uint TOKEN_ASSIGN_PRIMARY = 0x0001;
private const int GENERIC_ALL_ACCESS = 0x10000000;
private const int STARTF_USESHOWWINDOW = 0x00000001;
private const int STARTF_FORCEONFEEDBACK = 0x00000040;
private const uint CREATE_UNICODE_ENVIRONMENT = 0x00000400;
private const int STD_INPUT_HANDLE = -10;
private static readonly IntPtr INVALID_HANDLE_VALUE = (IntPtr)(-1);
private static readonly HandleRef NullHandleRef = new HandleRef(null, IntPtr.Zero);
private static string LaunchProcessAsUser(string cmdLine, IntPtr token, IntPtr envBlock)
{
bool result = false;
PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
SECURITY_ATTRIBUTES saProcess = new SECURITY_ATTRIBUTES();
SECURITY_ATTRIBUTES saThread = new SECURITY_ATTRIBUTES();
saProcess.nLength = (uint)Marshal.SizeOf(saProcess);
saThread.nLength = (uint)Marshal.SizeOf(saThread);
IntPtr stdoutReadHandle = IntPtr.Zero;
SafeFileHandle safeHandle = null;
IntPtr stdoutWriteHandle = IntPtr.Zero;
IntPtr stdinHandle = GetStdHandle(STD_INPUT_HANDLE);
try
{
CreatePipe(out stdoutReadHandle, out stdoutWriteHandle, false);
SetHandleInformation(stdoutReadHandle, HANDLE_FLAGS.INHERIT, 0);
STARTUPINFO si = new STARTUPINFO();
si.cb = (uint)Marshal.SizeOf(si);
//if this member is NULL, the new process inherits the desktop
//and window station of its parent process. If this member is
//an empty string, the process does not inherit the desktop and
//window station of its parent process; instead, the system
//determines if a new desktop and window station need to be created.
//If the impersonated user already has a desktop, the system uses the
//existing desktop.
si.lpDesktop = #"WinSta0\Default"; //Modify as needed
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_FORCEONFEEDBACK;
si.wShowWindow = SW_SHOW;
si.hStdInput = stdinHandle;
si.hStdOutput = stdoutWriteHandle;
si.hStdOutput = stdoutWriteHandle;
//Set other si properties as required.
result = CreateProcessAsUser(
token,
null,
cmdLine,
ref saProcess,
ref saThread,
false,
CREATE_UNICODE_ENVIRONMENT,
envBlock,
null,
ref si,
out pi);
if (result == false)
{
int error = Marshal.GetLastWin32Error();
string message = String.Format("CreateProcessAsUser Error: {0}", error);
Debug.WriteLine(message);
}
var ret = WaitForSingleObject(pi.hProcess, 100000);
//Console.Write("WaitForSingleObject returned " + ret);
//ret==258 (0x102) - not signalled, ret==0 ok!
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
CloseHandle(stdoutWriteHandle);
CloseHandle(stdinHandle);
safeHandle = new SafeFileHandle(stdoutReadHandle, true);
string outputData;
var encoding = Encoding.GetEncoding(GetConsoleOutputCP());
using (var fs = new FileStream(safeHandle, FileAccess.Read, 0x1000, true))
using (var reader = new StreamReader(fs, encoding))
{
outputData = reader.ReadToEnd();
}
return outputData;
}
finally
{
if (!safeHandle.IsClosed)
{
safeHandle.Close();
}
}
}
private static IntPtr GetPrimaryToken(int processId)
{
IntPtr token = IntPtr.Zero;
IntPtr primaryToken = IntPtr.Zero;
bool retVal = false;
Process p = null;
try
{
p = Process.GetProcessById(processId);
}
catch (ArgumentException ex)
{
string details = String.Format("ProcessID {0} Not Available, More: {1}", processId, ex.Message);
throw new ArgumentException(details);
}
//Gets impersonation token
retVal = OpenProcessToken(p.Handle, TOKEN_DUPLICATE, ref token);
if (retVal == true)
{
SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
sa.nLength = (uint)Marshal.SizeOf(sa);
//Convert the impersonation token into Primary token
retVal = DuplicateTokenEx(
token,
TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_QUERY,
ref sa,
(int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,
(int)TOKEN_TYPE.TokenPrimary,
ref primaryToken);
//Close the Token that was previously opened.
CloseHandle(token);
if (retVal == false)
{
string message = String.Format("DuplicateTokenEx Error: {0}", Marshal.GetLastWin32Error());
throw new Exception(message);
}
}
else
{
string message = String.Format("OpenProcessToken Error: {0}", Marshal.GetLastWin32Error());
Debug.WriteLine(message);
}
//We'll Close this token after it is used.
return primaryToken;
}
private static IntPtr GetEnvironmentBlock(IntPtr token)
{
IntPtr envBlock = IntPtr.Zero;
bool retVal = CreateEnvironmentBlock(ref envBlock, token, false);
if (retVal == false)
{
//Environment Block, things like common paths to My Documents etc.
//Will not be created if "false"
//It should not adversley affect CreateProcessAsUser.
string message = String.Format("CreateEnvironmentBlock Error: {0}", Marshal.GetLastWin32Error());
Debug.WriteLine(message);
}
return envBlock;
}
public static string Launch(string appCmdLine /*,int processId*/)
{
string ret = "";
//Either specify the processID explicitly
//Or try to get it from a process owned by the user.
//In this case assuming there is only one explorer.exe
Process[] ps = Process.GetProcessesByName("explorer");
int processId = -1;//=processId
if (ps.Length > 0)
{
processId = ps[0].Id;
}
if (processId > 1)
{
IntPtr token = GetPrimaryToken(processId);
if (token != IntPtr.Zero)
{
IntPtr envBlock = GetEnvironmentBlock(token);
ret = LaunchProcessAsUser(appCmdLine, token, envBlock);
if (envBlock != IntPtr.Zero)
DestroyEnvironmentBlock(envBlock);
CloseHandle(token);
}
}
return ret;
}
private static void CreatePipe(out IntPtr parentHandle, out IntPtr childHandle, bool parentInputs)
{
string pipename = #"\\.\pipe\" + Guid.NewGuid().ToString();
parentHandle = CreateNamedPipe(pipename, 0x40000003, 0, 0xff, 0x1000, 0x1000, 0, IntPtr.Zero);
if (parentHandle == INVALID_HANDLE_VALUE)
{
throw new Exception("Invalid Handle Exception.");
}
int childAcc = 0x40000000;
if (parentInputs)
{
childAcc = -2147483648;
}
childHandle = CreateFile(pipename, childAcc, 3, IntPtr.Zero, 3, 0x40000080, NullHandleRef);
if (childHandle == INVALID_HANDLE_VALUE)
{
throw new Exception("Invalid Handle Exception.");
}
}
}
}
NOTE: It is a sample code only, In real code I want to take a screenshot of the desktop from Window Service using another process, In the context of explorer.exe, because windows service cannot access to desktop directly and need another context to do it.
Same thread:https://social.msdn.microsoft.com/Forums/vstudio/en-US/19ff0bb0-924f-4f9c-9b71-ff4ee4ccaf50/standard-output-from-createprocessasuser-always-empty-in-c?forum=csharpgeneral#19ff0bb0-924f-4f9c-9b71-ff4ee4ccaf50
If you need more information please tell me, thanks!

Getting command line of a process without WMI

I found wj32 post about how to read the command line of specified process via WinAPI. I try to translate that example on C# and I've a few questions. I can get a valid pointer to the field CommandLine of RTL_USER_PROCESS_PARAMETERS structure but the difficalty is getting the string itself. Should I use unsafe code? How to correctly get CommandLine of a process using wj32's example?
using System;
using System.Runtime.InteropServices;
namespace CommandLine {
internal static class NativeMethods {
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern Boolean CloseHandle(
IntPtr hObject
);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern IntPtr OpenProcess(
UInt32 dwDesiredAccess,
[MarshalAs(UnmanagedType.Bool)]
Boolean bInheritHandle,
Int32 dwProcessId
);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern Boolean ReadProcessMemory(
IntPtr hProcess,
IntPtr lpBaseAddress,
out IntPtr lpBuffer,
Int32 nSize,
out IntPtr lpNumberOfBytesRead
);
[DllImport("ntdll.dll")]
internal static extern Int32 NtQueryInformationProcess(
IntPtr ProcessHandle,
UInt32 ProcessInformationClass,
ref PROCESS_BASIC_INFORMATION ProcessInformation,
UInt32 ProcessInformationLength,
IntPtr ReturnLength
);
[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_BASIC_INFORMATION {
internal Int32 ExitProcess;
internal IntPtr PebBaseAddress;
internal IntPtr AffinityMask;
internal Int32 BasePriority;
internal IntPtr UniqueProcessId;
internal IntPtr InheritedFromUniqueProcessId;
internal UInt32 Size {
get { return (UInt32)Marshal.SizeOf(typeof(PROCESS_BASIC_INFORMATION)); }
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct UNICODE_STRING {
internal UInt16 Length;
internal UInt16 MaximumLength;
[MarshalAs(UnmanagedType.LPWStr)]
internal String Buffer;
}
}
internal sealed class Program {
private const UInt32 PROCESS_QUERY_INFORMATION = 0x400;
private const UInt32 PROCESS_VM_READ = 0x010;
[STAThread()]
static void Main(String[] args) {
if (args.Length != 1) return;
Int32 pid;
if (!Int32.TryParse(args[0], out pid)) return;
IntPtr proc;
NativeMethods.PROCESS_BASIC_INFORMATION pbi = new NativeMethods.PROCESS_BASIC_INFORMATION();
IntPtr rupp; //RTL_USER_PROCESS_PARAMETERS
IntPtr cmdl; //CommandLine field
IntPtr read;
if ((proc = NativeMethods.OpenProcess(
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid
)) == IntPtr.Zero) return;
if (NativeMethods.NtQueryInformationProcess(proc, 0, ref pbi, pbi.Size, IntPtr.Zero) == 0) {
if (NativeMethods.ReadProcessMemory(
proc, (IntPtr)(pbi.PebBaseAddress.ToInt32() + 0x10), out rupp, IntPtr.Size, out read
)) {
if (NativeMethods.ReadProcessMemory(
proc, (IntPtr)(rupp.ToInt32() + 0x40), out cmdl,
Marshal.SizeOf(typeof(NativeMethods.UNICODE_STRING)), out read
)) {
// what I need to do to get command line?
}
}
}
NativeMethods.CloseHandle(proc);
}
}
}
OK, I've tested this version on a couple of processes. Note that it works on 32-bit processes only as the memory layout is different for 64-bit. I'm short of time to write up why I've made the changes I have but hopefully you can work with this. If I get some time I'll come back and update the answer
using System;
using System.Runtime.InteropServices;
namespace CommandLine
{
internal static class NativeMethods
{
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern Boolean CloseHandle(
IntPtr hObject
);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern IntPtr OpenProcess(
UInt32 dwDesiredAccess,
[MarshalAs(UnmanagedType.Bool)]
Boolean bInheritHandle,
Int32 dwProcessId
);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern Boolean ReadProcessMemory(
IntPtr hProcess,
IntPtr lpBaseAddress,
byte[] lpBuffer,
Int32 nSize,
out IntPtr lpNumberOfBytesRead
);
[DllImport("ntdll.dll")]
internal static extern Int32 NtQueryInformationProcess(
IntPtr ProcessHandle,
UInt32 ProcessInformationClass,
ref PROCESS_BASIC_INFORMATION ProcessInformation,
UInt32 ProcessInformationLength,
IntPtr ReturnLength
);
[StructLayout(LayoutKind.Sequential, Pack=1)]
internal struct PROCESS_BASIC_INFORMATION
{
internal Int32 ExitProcess;
internal IntPtr PebBaseAddress;
internal IntPtr AffinityMask;
internal Int32 BasePriority;
internal IntPtr UniqueProcessId;
internal IntPtr InheritedFromUniqueProcessId;
internal UInt32 Size
{
get { return (UInt32)Marshal.SizeOf(typeof(PROCESS_BASIC_INFORMATION)); }
}
}
[StructLayout(LayoutKind.Sequential, Pack=1)]
internal struct UNICODE_STRING
{
internal UInt16 Length;
internal UInt16 MaximumLength;
internal IntPtr buffer;
}
}
internal sealed class Program
{
private const UInt32 PROCESS_QUERY_INFORMATION = 0x400;
private const UInt32 PROCESS_VM_READ = 0x010;
[STAThread()]
static void Main(String[] args)
{
if (args.Length != 1) return;
Int32 pid;
if (!Int32.TryParse(args[0], out pid)) return;
IntPtr proc;
NativeMethods.PROCESS_BASIC_INFORMATION pbi = new NativeMethods.PROCESS_BASIC_INFORMATION();
IntPtr read;
if ((proc = NativeMethods.OpenProcess(
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid
)) == IntPtr.Zero) return;
if (NativeMethods.NtQueryInformationProcess(proc, 0, ref pbi, pbi.Size, IntPtr.Zero) == 0)
{
byte[] rupp = new byte[IntPtr.Size];
if (NativeMethods.ReadProcessMemory(
proc, (IntPtr)(pbi.PebBaseAddress.ToInt32() + 0x10), rupp, IntPtr.Size, out read
))
{
Int32 ruppPtr = BitConverter.ToInt32(rupp,0);
byte[] cmdl = new byte[Marshal.SizeOf(typeof(NativeMethods.UNICODE_STRING))];
if (NativeMethods.ReadProcessMemory(
proc, (IntPtr)(ruppPtr + 0x40), cmdl,
Marshal.SizeOf(typeof(NativeMethods.UNICODE_STRING)), out read
))
{
NativeMethods.UNICODE_STRING ucsData;
ucsData = ByteArrayToStructure<NativeMethods.UNICODE_STRING>(cmdl);
byte[] parms =new byte[ucsData.Length];
if (NativeMethods.ReadProcessMemory(
proc, ucsData.buffer, parms,
ucsData.Length, out read
))
{
var s = System.Text.Encoding.Unicode.GetString(parms);
Console.WriteLine("Parameters = {0}", s);
}
}
}
}
NativeMethods.CloseHandle(proc);
}
static T ByteArrayToStructure<T>(byte[] bytes) where T : struct
{
GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
T stuff = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
handle.Free();
return stuff;
}
}
}

Reading from Protected Process Memory

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;
}
}

How to scroll Window in other process with Win32API

Im trying to create a program where I can send some process id of a process (that may be firefox, ie, notepad etc) to a method that scrolls window of the process.
I have been trying with GetScrollBarInfo and SetScrollPos which I found at pinvoke without any success. Im not sure if this is the right way or not. I started playing with GetScrollBarInfo, but it doesn't seem to work.
I tried the code found at http://www.pinvoke.net/default.aspx/user32.getscrollbarinfo
[StructLayout(LayoutKind.Sequential)]
public struct SCROLLBARINFO
{
public int cbSize;
public Rectangle rcScrollBar;
public int dxyLineButton;
public int xyThumbTop;
public int xyThumbBottom;
public int reserved;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public int[] rgstate;
}
private const uint OBJID_HSCROLL = 0xFFFFFFFA;
private const uint OBJID_VSCROLL = 0xFFFFFFFB;
private const uint OBJID_CLIENT = 0xFFFFFFFC;
private int Scroll(int ProcessID)
{
IntPtr handle = Process.GetProcessById(ProcessID).MainWindowHandle;
SCROLLBARINFO psbi = new SCROLLBARINFO();
psbi.cbSize = Marshal.SizeOf(psbi);
int nResult = GetScrollBarInfo(handle, OBJID_CLIENT, ref psbi);
if (nResult == 0)
{
int nLatError = Marshal.GetLastWin32Error();
}
}
GetLastWin32Error() returns errorcode 122 which means "The data area passed to a system call is too small", according to http://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx
Im not sure what I do wrong. How can I solve this?
You could send a WM_MOUSEWHEEL message to do what you want. For example, to scroll down once in a new notepad window using C++:
HWND hwnd = FindWindowEx(FindWindow(NULL, "Untitled - Notepad"), NULL, "Edit", NULL);
RECT r;
GetClientRect(hwnd, &r);
SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(0, WHEEL_DELTA * -1), MAKELPARAM(r.right / 2, r.bottom / 2));
To adapt that to C#, you could do something such as this:
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, Int32 wParam, ref Point lParam);
private void ScrollWindow(IntPtr hwnd, Point p, int scrolls = -1)
{
SendMessage(hwnd, WM_MOUSEWHEEL, (WHEEL_DELTA * scrolls) << 16, ref p);
}
Which could be used to scroll down once in a new notepad window like this:
//Imports
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
...
//Actual code
IntPtr hwnd = FindWindowEx(FindWindow(null, "Untitled - Notepad"), IntPtr.Zero, "Edit", null);
Point p = new Point(0, 0);
ScrollWindow(hwnd, p);
Some programs will require the lParam sent to be a point that's actually above the scrolled area, while others such as notepad will not.
If you're trying to scroll the window of another process, you need to, in effect, simulate clicks on the scroll bar or key presses. The cleanest way to do that is to use UI Automation, which has both .NET and native interfaces.
By asking for the scrollbar info, you're simply getting information about how the scrollbar is displayed. That's not going to give you a way to scroll the window content. You have to get the target application to scroll the content by making it think the user is operating the scrollbar.
http://forums.codeguru.com/showthread.php?446352-How-to-scroll-active-window-SendMessage&p=1686041#post1686041
Final Code
class Program
{
[DllImport("user32.dll")]
static extern bool GetGUIThreadInfo(uint idThread, ref GUITHREADINFO lpgui);
[DllImport("user32.dll")]
public static extern int SetScrollPos(IntPtr hWnd, System.Windows.Forms.Orientation nBar, int nPos, bool bRedraw);
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);
public struct GUITHREADINFO
{
public int cbSize;
public int flags;
public IntPtr hwndActive;
public IntPtr hwndFocus;
public IntPtr hwndCapture;
public IntPtr hwndMenuOwner;
public IntPtr hwndMoveSize;
public IntPtr hwndCaret;
public System.Drawing.Rectangle rcCaret;
}
const Int32 WM_VSCROLL = 0x0115;
const Int32 SB_LINERIGHT = 1;
static void Main(string[] args)
{
//create process in focus
Process.Start("notepad++", "Source.cpp");
Thread.Sleep(1000);
GUITHREADINFO threadInfo = new GUITHREADINFO();
threadInfo.cbSize = Marshal.SizeOf(threadInfo);
GetGUIThreadInfo(0, ref threadInfo);
SendMessage(threadInfo.hwndFocus, WM_VSCROLL, SB_LINERIGHT, 0);
//SetScrollPos not work. Change only scrollbar without scroll window
//SetScrollPos(threadInfo.hwndFocus, System.Windows.Forms.Orientation.Vertical, 10, true);
}
}

Categories

Resources