Sending IOCTL to 64bit machine in C# - c#

I'm sending IOCTL's to my keyboard filter driver and the code is as follows:
Guid GUID_DEVINTERFACE_KBFILTER = new Guid(0x3fb7299d, 0x6847, 0x4490, 0xb0, 0xc9, 0x99, 0xe0, 0x98, 0x6a, 0xb8, 0x86);
IntPtr handle = SetupDiGetClassDevs(ref GUID_DEVINTERFACE_KBFILTER, IntPtr.Zero, IntPtr.Zero, (int)(DiGetClassFlags.DIGCF_PRESENT | DiGetClassFlags.DIGCF_DEVICEINTERFACE));
if (handle != INVALID_HANDLE_VALUE)
{
bool Success = true;
int i = 0;
while (Success)
{
SP_DEVICE_INTERFACE_DATA deviceInterfaceData = new SP_DEVICE_INTERFACE_DATA();
deviceInterfaceData.cbSize = Marshal.SizeOf(deviceInterfaceData);
// start the enumeration
Success = SetupDiEnumDeviceInterfaces(handle, IntPtr.Zero, ref GUID_DEVINTERFACE_KBFILTER, (uint)i, ref deviceInterfaceData);
if (Success)
{
// build a DevInfo Data structure
SP_DEVINFO_DATA devInfoData = new SP_DEVINFO_DATA();
devInfoData.cbSize = (uint)Marshal.SizeOf(devInfoData);
// build a Device Interface Detail Data structure
deviceInterfaceDetailData = new SP_DEVICE_INTERFACE_DETAIL_DATA();
deviceInterfaceDetailData.cbSize = 4 + Marshal.SystemDefaultCharSize;
// now we can get some more detailed information
uint nRequiredSize = 0;
int nBytes = BUFFER_SIZE;
if (SetupDiGetDeviceInterfaceDetail(handle, ref deviceInterfaceData, ref deviceInterfaceDetailData, (uint)nBytes, out nRequiredSize, ref devInfoData))
{
uint ptrPrevious;
CM_Get_Parent(out ptrPrevious, devInfoData.DevInst, 0);
// Now we get the InstanceID of the USB level device
IntPtr ptrInstanceBuf = Marshal.AllocHGlobal(nBytes);
CM_Get_Device_ID(ptrPrevious, ptrInstanceBuf, nBytes, 0);
string InstanceID = Marshal.PtrToStringAuto(ptrInstanceBuf);
Marshal.FreeHGlobal(ptrInstanceBuf);
}
}
i++;
}
}
SetupDiDestroyDeviceInfoList(handle);
if (string.IsNullOrEmpty(deviceInterfaceDetailData.DevicePath))
{
return false;
}
The code after this works fine. The problem lies here and this works on a 32bit machine but does not work on 64 bit machines.In 64bit machines, deviceInterfaceDetailData.DevicePath is empty where as in 32 bit machines I get a valid device path.Is there something wrong in the build process?

I got the solution to this issue. The problem was the deviceInterfaceDetailData.cbSize value set. According to http://www.pinvoke.net/default.aspx/setupapi.setupdigetdeviceinterfacedetail I changed the cbSize value to 8 for 64 bit machines and then it works!

Related

C# Get list of handles, AcessViolationException

Info:
.Net 4.5
Tested on:
Win7 64 bit
Win10 64 bit (Virtual Box)
I am trying to get a list of handles of an external process and return their names as string so I can close a specific one afterwards. Therefore i wrote this function using the Win32API which will check if the handle is the handle i want to close: `
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);
Win32API.CloseHandle(ipHandle);
return strObjectTypeName;
}`
The problem however is that this code works in Win7 64bit, not in Win10! --> In Win 10 strObjectTypeName = Marshal.PtrToStringUni(); throws a AcessViolationException (Last few lines in the code)
System.AccessViolationException Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
Am I missing something here about how unmanaged memory has to be accessed in win10?
I have just come across the same issue. I haven't tried Win7, but when you run the code on Win10(x64) as 32bit (e.g. set the "Prefer 32-bit flag" of your application) it should work.
When the exception happens, drag&drop the variable "ipTemp" to the "memory window" of Visual Studio, if it displays only question marks or an error message you don't have a valid pointer.
As far as I figured out, there are (more) padding bytes in the 64bit versions of the structs that are used by this API:
OBJECT_TYPE_INFORMATION contains a UNICODE_STRING and UNICODE_STRING has 4 padding bytes before the Buffer-field in 64bit Mode.
My workaraound was this:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct UNICODE_STRING
{
private IntPtr _dummy; // the two ushorts seem to be padded with 4 bytes in 64bit mode only
/// <summary>
/// The length, in bytes, of the string stored in Buffer. If the string is null-terminated, Length does not include the trailing null character.
/// </summary>
public ushort Length
{
get { return (ushort)Marshal.ReadInt16(this, 0); }
}
/// <summary>
/// The length, in bytes, of Buffer.
/// </summary>
public ushort MaximumLength
{
get { return (ushort)Marshal.ReadInt16(this, 2); }
}
public IntPtr Buffer;
}
During my research I found so many questions regarding this topic and basically two kinds of sample code
that have been copied all over the Internet that I am considering to create an open-source library named WinKernelObjectsDotNet.
Update: The library is now available here. It supports finding the process that is locking a file or a serial-port (COM) with a single line of code.
I suggest to change UNICODE_STRING structure in a such way.
public struct UNICODE_STRING
{
public ushort Length;
public ushort MaximumLength;
[MarshalAs(UnmanagedType.LPWStr)] public string Buffer;
}
So getObjectTypeName method will be something like this and will work for both 32/64:
public static string getObjectTypeName(SYSTEM_HANDLE_INFORMATION shHandle, Process process) {
IntPtr ipProcessHwnd = OpenProcess(ProcessAccessFlags.All, false, process.Id);
if (!DuplicateHandle(ipProcessHwnd, shHandle.Handle, GetCurrentProcess(), out IntPtr ipHandle, 0, false, DUPLICATE_SAME_ACCESS)) {
return null;
}
OBJECT_BASIC_INFORMATION objBasicInformation = new OBJECT_BASIC_INFORMATION();
IntPtr ipBasicInformation = Marshal.AllocHGlobal(Marshal.SizeOf(objBasicInformation));
int iBasicInformationLength = 0;
NtQueryObject(ipHandle, (int) ObjectInformationClass.ObjectBasicInformation, ipBasicInformation, Marshal.SizeOf(objBasicInformation), ref iBasicInformationLength);
objBasicInformation = (OBJECT_BASIC_INFORMATION) Marshal.PtrToStructure(ipBasicInformation, typeof(OBJECT_BASIC_INFORMATION));
Marshal.FreeHGlobal(ipBasicInformation);
int iObjectTypeInformationLength = objBasicInformation.TypeInformationLength;
IntPtr ipObjectTypeInformation = Marshal.AllocHGlobal(iObjectTypeInformationLength);
while (Win32API.STATUS_INFO_LENGTH_MISMATCH == (uint) (NtQueryObject(ipHandle, (int) ObjectInformationClass.ObjectTypeInformation, ipObjectTypeInformation, iObjectTypeInformationLength, ref iObjectTypeInformationLength))) {
Marshal.FreeHGlobal(ipObjectTypeInformation);
ipObjectTypeInformation = Marshal.AllocHGlobal(iObjectTypeInformationLength);
}
CloseHandle(ipHandle);
OBJECT_TYPE_INFORMATION objObjectType = (OBJECT_TYPE_INFORMATION)Marshal.PtrToStructure(ipObjectTypeInformation, typeof(OBJECT_TYPE_INFORMATION));
Marshal.FreeHGlobal(ipObjectTypeInformation);
return objObjectType.Name.Buffer;
}

How to retrieve the vendor and product ID's using the SetupDiGetDeviceInterfaceDetail API

I'm trying to find the vendor id and the product id of a USB card. For this purpose, I use setupapi.dll. In my code, I use the SetupDiGetDeviceInterfaceDetail call twice and on the second time, the function returns true with no error, but I don't know what I can do after that.
My code:
result = HidD_GetHidGuid(ref HidGuid);
DeviceInfoSet = SetupDiGetClassDevs(ref HidGuid, IntPtr.Zero, IntPtr.Zero, 18);
do
{
SP_DEVICE_INTERFACE_DATA MyDeviceInterfaceData = new SP_DEVICE_INTERFACE_DATA();
MyDeviceInterfaceData.cbSize = 28;
result = SetupDiEnumDeviceInterfaces(DeviceInfoSet, IntPtr.Zero, ref HidGuid,
MemberIndex, ref MyDeviceInterfaceData);
if (result != 0)
{
result = SetupDiGetDeviceInterfaceDetail(DeviceInfoSet, ref MyDeviceInterfaceData,
IntPtr.Zero, 0, out neededSize, IntPtr.Zero);
//int lastError = Marshal.GetLastWin32Error();
int requiredSize = (int)neededSize;
// build a DevInfo Data structure
SP_DEVINFO_DATA da = new SP_DEVINFO_DATA();
da.cbSize = Marshal.SizeOf(da);
SP_DEVICE_INTERFACE_DETAIL_DATA MyDeviceInterfaceDetailData = new
SP_DEVICE_INTERFACE_DETAIL_DATA();
if (IntPtr.Size == 8) // for 64 bit operating systems
MyDeviceInterfaceDetailData.cbSize = 8;
else
MyDeviceInterfaceDetailData.cbSize = 4 + Marshal.SystemDefaultCharSize;
// for 32 bit systems
IntPtr detailDataBuffer = Marshal.AllocHGlobal(requiredSize);
Marshal.StructureToPtr(MyDeviceInterfaceDetailData, detailDataBuffer, false);
result = SetupDiGetDeviceInterfaceDetail(DeviceInfoSet, ref MyDeviceInterfaceData,
detailDataBuffer, requiredSize, out neededSize, IntPtr.Zero);
int lastError = Marshal.GetLastWin32Error();
When I try the following code, the result is "-" :
string instanceID = Marshal.PtrToStringAuto(detailDataBuffer);
How can I get the vendor ID and the product ID?
In the SP_DEVICE_INTERFACE_DETAIL_DATA struct you find the DevicePath, which is something like \?\hid#vid_045e&pid_00f0#7&13ac544b&0&0000#{GUID}. Just fiddle out the vid_045e and pid_00f0 part.

C# How to get capability HardwareDisabled property for hardware device

I am trying to tell if a hardware device (specifically multitouch digitizer) is disabled.
I can successfully enable/disable the device using the DisableHardware class from here.
I have found the DEVICE_CAPABILITIES struct with property HardwareDisabled. But if I call SetupDiGetDeviceRegistryPropertyW, to return SPDRP_CAPABILITIES, I am unable to convert the returned value (DWORD) to useful uint.
I believe my conversion is wrong from byte[] => uint, I always get 128.
private static bool IsDeviceHardwareDisabeld(IntPtr info, SP_DEVINFO_DATA devdata)
{
Trace.WriteLine("IsDeviceHardwareDisabeld");
uint SPDRP_CAPABILITIES = 0x0000000F;
uint CM_DEVCAP_HARDWAREDISABLED = 16384u;
uint propId = SPDRP_CAPABILITIES;
uint proptype, outsize;
IntPtr buffer = IntPtr.Zero;
try
{
uint buflen = 512;
buffer = Marshal.AllocHGlobal((int)buflen);
outsize = 0;
SetupDiGetDeviceRegistryPropertyW(
info,
ref devdata,
propId,
out proptype,
buffer,
buflen,
ref outsize);
Trace.WriteLine("OUTSIZE: " + outsize.ToString()); // 4
byte[] lbuffer = new byte[outsize];
Marshal.Copy(buffer, lbuffer, 0, (int)outsize);
int errcode = Marshal.GetLastWin32Error();
if (errcode == ERROR_INVALID_DATA) throw new Exception("ERROR_INVALID_DATA");
CheckError("SetupDiGetDeviceRegistryPropertyW", errcode);
uint capabilities = BitConverter.ToUInt32(lbuffer, 0); // always 128
uint bitwise = capabilities & CM_DEVCAP_HARDWAREDISABLED; // always 0
bool isHardwareDisabled = bitwise > 0;
Trace.WriteLine("HARDWAREDISABLED: " + isHardwareDisabled.ToString());
return isHardwareDisabled;
}
finally
{
if (buffer != IntPtr.Zero)
Marshal.FreeHGlobal(buffer);
}
}
My conversion always returns 128 for value of DEVICE_CAPABILITIES.

WriteFile returning error code 87

I am working on a program that is writing to an HID device and am getting the error 87, Invalid parameter on the WriteFile function. I got the functions from Jan Axelson's USB Complete so I'm not sure why I am getting the error.
I am using this to find my device:
private void USBInit()
{
IntPtr deviceInfoSet;
Int32 memberIndex = 0;
SP_DEVICE_INTERFACE_DATA MyDeviceInterfaceData = new SP_DEVICE_INTERFACE_DATA();
Int32 bufferSize = 0;
IntPtr detailDataBuffer;
Boolean success = false;
deviceFound = false;
HidD_GetHidGuid(ref hidGuid); // Get the GUID
deviceInfoSet = SetupDiGetClassDevs // Get pointer to a device info set
(ref hidGuid,
IntPtr.Zero,
IntPtr.Zero,
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
do
{
MyDeviceInterfaceData.cbSize = Marshal.SizeOf(MyDeviceInterfaceData); // Identify Device Interface
success = SetupDiEnumDeviceInterfaces
(deviceInfoSet,
IntPtr.Zero,
ref hidGuid,
memberIndex,
ref MyDeviceInterfaceData);
success = SetupDiGetDeviceInterfaceDetail // Request Structure with Device Path Name
(deviceInfoSet,
ref MyDeviceInterfaceData,
IntPtr.Zero,
0,
ref bufferSize,
IntPtr.Zero);
detailDataBuffer = Marshal.AllocHGlobal(bufferSize);
Marshal.WriteInt32(detailDataBuffer, (IntPtr.Size == 4) ? (4 + Marshal.SystemDefaultCharSize) : 8);
success = SetupDiGetDeviceInterfaceDetail
(deviceInfoSet,
ref MyDeviceInterfaceData,
detailDataBuffer,
bufferSize,
ref bufferSize,
IntPtr.Zero);
IntPtr pDevicePathName = new IntPtr(detailDataBuffer.ToInt32() + 4);
devicePathName = Marshal.PtrToStringAuto(pDevicePathName);
Marshal.FreeHGlobal(detailDataBuffer);
/* Request Communications Handle */
deviceHandle = CreateFile
(devicePathName,
(GENERIC_WRITE | GENERIC_READ),
FILE_SHARE_READ | FILE_SHARE_WRITE,
IntPtr.Zero,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
0);
/* Get Vendor ID and Product ID */
DeviceAttributes.Size = Marshal.SizeOf(DeviceAttributes);
success = HidD_GetAttributes(deviceHandle, ref DeviceAttributes);
// Compare Vendor ID and Product ID.
if ((DeviceAttributes.VendorID == myVendorID) && (DeviceAttributes.ProductID == myProductID))
{
MessageBoxResult res = System.Windows.MessageBox.Show("Device Found", "K-IX", MessageBoxButton.OK);
deviceFound = true;
/* Get pointer to capabilities */
success = HidD_GetPreparsedData(deviceHandle, ref preparsedData);
/* Get Device Capabilities */
Int32 result = 0;
result = HidP_GetCaps(preparsedData, ref Capabilities);
return;
}
else
{
// Not my device
memberIndex++;
deviceHandle.Close();
if (memberIndex == 128)
{ break; }
}
} while (!deviceFound);
}
And this is the code I am using to try to send to the device:
private void SendUSB()
{
Int32 numberOfBytesWritten = 0;
Byte[] outputReportBuffer = null;
Boolean success;
Boolean success2;
// Set size of the Output report buffer.
Array.Resize(ref outputReportBuffer, Capabilities.InputReportByteLength);
// Store Report ID in first byte of header.
outputReportBuffer[0] = 0;
// Store report data following the Report ID.
outputReportBuffer[1] = 0x01;
//outputReportBuffer[2] = 0x02;
// outputReportBuffer[3] = 0x03;
// Send Report
success = WriteFile
(deviceHandle,
outputReportBuffer,
outputReportBuffer.Length,
ref numberOfBytesWritten,
IntPtr.Zero);
if (!success)
{
Int32 lastError = Marshal.GetLastWin32Error();
}
success2 = HidD_FreePreparsedData(preparsedData);
}
I have verified that my report length expected for the device is 2, but am not sure where to go from here as USB and HID programming are new for me.
You specified FILE_FLAG_OVERLAPPED in the CreateFile() call but pass a null for the lpOverlapped argument in the WriteFile() call. That's not legal.
Remove the FILE_FLAG_OVERLAPPED option.

Calling CryptUIWizDigitalSign from .NET on x64

I am trying to digitally sign files using the CryptUIWizDigitalSign function from a .NET 2.0 application compiled to AnyCPU. The call works fine when running on x86 but fails on x64, it also works on an x64 OS when compiled to x86. Any idea on how to better marshall or call from x64?
The Win32exception returned is "Error encountered during digital signing of the file ..." with a native error code of -2146762749.
The relevant portion of the code are:
[StructLayout(LayoutKind.Sequential)]
public struct CRYPTUI_WIZ_DIGITAL_SIGN_INFO {
public Int32 dwSize;
public Int32 dwSubjectChoice;
[MarshalAs(UnmanagedType.LPWStr)]
public string pwszFileName;
public Int32 dwSigningCertChoice;
public IntPtr pSigningCertContext;
[MarshalAs(UnmanagedType.LPWStr)]
public string pwszTimestampURL;
public Int32 dwAdditionalCertChoice;
public IntPtr pSignExtInfo;
}
[DllImport("Cryptui.dll", CharSet=CharSet.Unicode, SetLastError=true)]
public static extern bool CryptUIWizDigitalSign(int dwFlags, IntPtr hwndParent, string pwszWizardTitle, ref CRYPTUI_WIZ_DIGITAL_SIGN_INFO pDigitalSignInfo, ref IntPtr ppSignContext);
CRYPTUI_WIZ_DIGITAL_SIGN_INFO digitalSignInfo = new CRYPTUI_WIZ_DIGITAL_SIGN_INFO();
digitalSignInfo = new CRYPTUI_WIZ_DIGITAL_SIGN_INFO();
digitalSignInfo.dwSize = Marshal.SizeOf(digitalSignInfo);
digitalSignInfo.dwSubjectChoice = 1;
digitalSignInfo.dwSigningCertChoice = 1;
digitalSignInfo.pSigningCertContext = pSigningCertContext;
digitalSignInfo.pwszTimestampURL = timestampUrl;
digitalSignInfo.dwAdditionalCertChoice = 0;
digitalSignInfo.pSignExtInfo = IntPtr.Zero;
digitalSignInfo.pwszFileName = filepath;
CryptUIWizDigitalSign(1, IntPtr.Zero, null, ref digitalSignInfo, ref pSignContext));
And here is how the SigningCertContext is retrieved (minus various error handling)
public IntPtr GetCertContext(String pfxfilename, String pswd)
IntPtr hMemStore = IntPtr.Zero;
IntPtr hCertCntxt = IntPtr.Zero;
IntPtr pProvInfo = IntPtr.Zero;
uint provinfosize = 0;
try {
byte[] pfxdata = PfxUtility.GetFileBytes(pfxfilename);
CRYPT_DATA_BLOB ppfx = new CRYPT_DATA_BLOB();
ppfx.cbData = pfxdata.Length;
ppfx.pbData = Marshal.AllocHGlobal(pfxdata.Length);
Marshal.Copy(pfxdata, 0, ppfx.pbData, pfxdata.Length);
hMemStore = Win32.PFXImportCertStore(ref ppfx, pswd, CRYPT_USER_KEYSET);
pswd = null;
if (hMemStore != IntPtr.Zero) {
Marshal.FreeHGlobal(ppfx.pbData);
while ((hCertCntxt = Win32.CertEnumCertificatesInStore(hMemStore, hCertCntxt)) != IntPtr.Zero) {
if (Win32.CertGetCertificateContextProperty(hCertCntxt, CERT_KEY_PROV_INFO_PROP_ID, IntPtr.Zero, ref provinfosize))
pProvInfo = Marshal.AllocHGlobal((int)provinfosize);
else
continue;
if (Win32.CertGetCertificateContextProperty(hCertCntxt, CERT_KEY_PROV_INFO_PROP_ID, pProvInfo, ref provinfosize))
break;
}
}
finally {
if (pProvInfo != IntPtr.Zero)
Marshal.FreeHGlobal(pProvInfo);
if (hMemStore != IntPtr.Zero)
Win32.CertCloseStore(hMemStore, 0);
}
return hCertCntxt;
}
When applications are compiled with AnyCPU target, they will load as 32-bit on 32 bit OS, and 64-bit on 64 bit OS. You cannot load a 32-bit DLL from a 64-bit process.
You have said that it "works" when compiling to x86. Can you just do this? This leads me to believe that the cryptui.dll in your search path is a 32-bit DLL.

Categories