Windows API USB IO (winusb.dll) - c#

Edit: This question evolved over time. The basic question was about how to connect to a and read/write to/from a USB device in Windows. Eventually I answered the question with the help of #benvoigt.
I have written a Hid library which writes and reads to/from Hid USB devices. It works well. However, the device I am connecting to has switched from Hid access to vanilla USB. The Hid code does not work when connecting to the different type of device. My aim now is to connect to the USB interface (as opposed to the Hid interface) and read/write to/from it.
On all platforms that access USB, we have to query the interfaces that exist for the USB device, and then "claim" an interface for reading and writing. Here is some code from a LibUsbDotNet sample (LibUsb is a working C USB library and LibUsbDotNet wraps it) https://github.com/LibUsbDotNet/LibUsbDotNet/blob/master/src/Examples/Read.Write/ReadWrite.cs.
using (var context = new UsbContext())
{
context.SetDebugLevel(LogLevel.Info);
//Get a list of all connected devices
var usbDeviceCollection = context.List();
//Narrow down the device by vendor and pid
var selectedDevice = usbDeviceCollection.FirstOrDefault(d => d.ProductId == ProductId && d.VendorId == VendorId);
//Open the device
selectedDevice.Open();
//Get the first config number of the interface
selectedDevice.ClaimInterface(selectedDevice.Configs[0].Interfaces[0].Number);
//Open up the endpoints
var writeEndpoint = selectedDevice.OpenEndpointWriter(WriteEndpointID.Ep01);
var readEnpoint = selectedDevice.OpenEndpointReader(ReadEndpointID.Ep01);
//Create a buffer with some data in it
var buffer = new byte[64];
buffer[0] = 0x3f;
buffer[1] = 0x23;
buffer[2] = 0x23;
//Write three bytes
writeEndpoint.Write(buffer, 3000, out var bytesWritten);
var readBuffer = new byte[64];
//Read some data
readEnpoint.Read(readBuffer, 3000, out var readBytes);
}
}
I have a feeling that LibUsb is achieving the opening of interfaces/endpoints in C like this (https://github.com/libusb/libusb/blob/c6f3866414e8deeee19e8a9f10f20bde9cb408d3/libusb/os/windows_winusb.c#L2199) . This is where it calls Initialize: https://github.com/libusb/libusb/blob/c6f3866414e8deeee19e8a9f10f20bde9cb408d3/libusb/os/windows_winusb.c#L2225 which is where my code is failing.
A little snippet of information is that this is definitely a WinUSB device. I can see that here:
Based on other people's comments and sample code, I can see that I need to use winusb.dll. I am able to call CreateFile to get a handle from the device. According to other sample code I have seen, the next step is to call WinUsb_Initialize. However, when I call this, I get an error code of 8 (ERROR_NOT_ENOUGH_MEMORY). There is some information here https://learn.microsoft.com/en-us/windows/desktop/api/winusb/nf-winusb-winusb_initialize . But, I don't quite understand what it is asking me to do. This is my code so far:
public override async Task InitializeAsync()
{
Dispose();
if (string.IsNullOrEmpty(DeviceId))
{
throw new WindowsException($"{nameof(DeviceDefinition)} must be specified before {nameof(InitializeAsync)} can be called.");
}
_DeviceHandle = APICalls.CreateFile(DeviceId, (APICalls.GenericWrite | APICalls.GenericRead), APICalls.FileShareRead | APICalls.FileShareWrite, IntPtr.Zero, APICalls.OpenExisting, APICalls.FileAttributeNormal | APICalls.FileFlagOverlapped, IntPtr.Zero);
var errorCode = Marshal.GetLastWin32Error();
if (errorCode > 0) throw new Exception($"Write handle no good. Error code: {errorCode}");
if (_DeviceHandle.IsInvalid) throw new Exception("Device handle no good");
var isSuccess = WinUsbApiCalls.WinUsb_Initialize(_DeviceHandle, out var interfaceHandle);
errorCode = Marshal.GetLastWin32Error();
if (!isSuccess) throw new Exception($"Initialization failed. Error code: {errorCode}");
IsInitialized = true;
RaiseConnected();
}
You can clone the branch of this repo here: https://github.com/MelbourneDeveloper/Device.Net/tree/WindowsUsbDevice. Just run the Usb.Net.WindowsSample project.
I also tried this and got exactly the same result:
public override async Task InitializeAsync()
{
Dispose();
if (string.IsNullOrEmpty(DeviceId))
{
throw new WindowsException($"{nameof(DeviceDefinition)} must be specified before {nameof(InitializeAsync)} can be called.");
}
_DeviceHandle = APICalls.CreateFile(DeviceId, (APICalls.GenericWrite | APICalls.GenericRead), APICalls.FileShareRead | APICalls.FileShareWrite, IntPtr.Zero, APICalls.OpenExisting, APICalls.FileAttributeNormal | APICalls.FileFlagOverlapped, IntPtr.Zero);
var errorCode = Marshal.GetLastWin32Error();
if (errorCode > 0) throw new Exception($"Write handle no good. Error code: {errorCode}");
var interfaceHandle = new IntPtr();
var pDll = NativeMethods.LoadLibrary(#"C:\GitRepos\Device.Net\src\Usb.Net.WindowsSample\bin\Debug\net452\winusb.dll");
var pAddressOfFunctionToCall = NativeMethods.GetProcAddress(pDll, "WinUsb_Initialize");
var initialize = (WinUsb_Initialize)Marshal.GetDelegateForFunctionPointer(pAddressOfFunctionToCall, typeof(WinUsb_Initialize));
var isSuccess = initialize(_DeviceHandle, ref interfaceHandle);
errorCode = Marshal.GetLastWin32Error();
if (!isSuccess) throw new Exception($"Initialization failed. Error code: {errorCode}");
IsInitialized = true;
RaiseConnected();
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate bool WinUsb_Initialize(SafeFileHandle DeviceHandle, ref IntPtr InterfaceHandle);
I strongly believe that there is something wrong with the device's WinUSB implementation itself. It works with other libraries like LibUsb, UWP, and Android, but WinUsb doesn't seem to like it. I tried this library: https://github.com/madwizard-thomas/winusbnet and it also fails on the same call with the same error code. The line it fails on and gets error code 8 is here: https://github.com/madwizard-thomas/winusbnet/blob/8f62d751a99be1e31d34b91115715d60aeff2dfc/WinUSBNet/API/WinUSBDevice.cs#L225
What is wrong with my code here? Is there something I need to do to allocate memory for the WinUsb_Initialize call?
How should I use the winusb.dll Windows API? What API calls do I need to make to claim and interface or endpoint for reading and writing?
It would really help me if someone could point me to a simple C or C# sample that reads and writes to a USB device and actually works.
WinDBG output:
************* Path validation summary ************** Response Time (ms) Location Deferred
srv* Symbol search path is: srv* Executable search path is: ModLoad:
00000236157c0000 00000236157c8000 Usb.Net.WindowsSample.exe
ModLoad: 00007ffb62880000 00007ffb62a61000 ntdll.dll ModLoad:
00007ffb60f40000 00007ffb610d0000 C:\WINDOWS\System32\user32.dll
ModLoad: 00007ffb5ed00000 00007ffb5ed20000
C:\WINDOWS\System32\win32u.dll ModLoad: 00007ffb4e1b0000
00007ffb4e214000 C:\WINDOWS\SYSTEM32\MSCOREE.DLL ModLoad:
00007ffb612a0000 00007ffb612c8000 C:\WINDOWS\System32\GDI32.dll
onecore\windows\core\console\open\src\renderer\gdi\invalidate.cpp(121)\conhost.exe!00007FF7169FE2AF:
(caller: 00007FF7169FF414) ReturnHr(1) tid(4230) 80070578 Invalid
window handle. ModLoad: 00007ffb60990000 00007ffb60a42000
C:\WINDOWS\System32\KERNEL32.dll ModLoad: 00007ffb5f000000
00007ffb5f192000 C:\WINDOWS\System32\gdi32full.dll ModLoad:
00007ffb60d90000 00007ffb60f03000 C:\WINDOWS\System32\MSCTF.dll
ModLoad: 00007ffb5ed80000 00007ffb5eff3000
C:\WINDOWS\System32\KERNELBASE.dll ModLoad: 00007ffb60610000
00007ffb606d2000 C:\WINDOWS\System32\OLEAUT32.dll ModLoad:
00007ffb60f10000 00007ffb60f3d000 C:\WINDOWS\System32\IMM32.DLL
************* Path validation summary ************** Response Time (ms) Location Deferred
srv* Symbol search path is: srv* Executable search path is: ModLoad:
00007ff7169f0000 00007ff716a8f000 conhost.exe ModLoad:
00007ffb61340000 00007ffb62780000 C:\WINDOWS\System32\shell32.dll
ModLoad: 00007ffb5cd80000 00007ffb5cda9000
C:\WINDOWS\system32\dwmapi.dll ModLoad: 00007ffb62880000
00007ffb62a61000 ntdll.dll ModLoad: 00007ffb5fcc0000
00007ffb5fd09000 C:\WINDOWS\System32\cfgmgr32.dll ModLoad:
00007ffb5f530000 00007ffb5fc3d000
C:\WINDOWS\System32\windows.storage.dll
onecore\windows\core\console\open\src\renderer\gdi\invalidate.cpp(121)\conhost.exe!00007FF7169FE2AF:
(caller: 00007FF7169FF414) ReturnHr(2) tid(4230) 80070578 Invalid
window handle. ModLoad: 00007ffb61140000 00007ffb61191000
C:\WINDOWS\System32\shlwapi.dll ModLoad: 00007ffb60990000
00007ffb60a42000 C:\WINDOWS\System32\KERNEL32.DLL ModLoad:
00007ffb5ec30000 00007ffb5ec41000
C:\WINDOWS\System32\kernel.appcore.dll ModLoad: 00007ffb5ed80000
00007ffb5eff3000 C:\WINDOWS\System32\KERNELBASE.dll ModLoad:
00007ffb5ec10000 00007ffb5ec2f000 C:\WINDOWS\System32\profapi.dll
ModLoad: 00007ffb5ebc0000 00007ffb5ec0c000
C:\WINDOWS\System32\powrprof.dll ModLoad: 00007ffb5ebb0000
00007ffb5ebba000 C:\WINDOWS\System32\FLTLIB.DLL ModLoad:
00007ffb5f490000 00007ffb5f52f000
C:\WINDOWS\System32\msvcp_win.dll ModLoad: 00007ffb5f1a0000
00007ffb5f29a000 C:\WINDOWS\System32\ucrtbase.dll ModLoad:
00007ffb606e0000 00007ffb60789000 C:\WINDOWS\System32\shcore.dll
ModLoad: 00007ffb4e290000 00007ffb4e4f9000
C:\WINDOWS\WinSxS\amd64_microsoft.windows.common-controls_6595b64144ccf1df_6.0.17134.472_none_fb3f9af53068156d\comctl32.DLL
ModLoad: 00007ffb5ca60000 00007ffb5caf8000
C:\WINDOWS\system32\uxtheme.dll ModLoad: 00007ffb608f0000
00007ffb6098e000 C:\WINDOWS\System32\msvcrt.dll ModLoad:
00007ffb601e0000 00007ffb60304000 C:\WINDOWS\System32\RPCRT4.dll
ModLoad: 00007ffb60a60000 00007ffb60d82000
C:\WINDOWS\System32\combase.dll ModLoad: 00007ffb5fc40000
00007ffb5fcba000 C:\WINDOWS\System32\bcryptPrimitives.dll ModLoad:
00007ffb627a0000 00007ffb62841000 C:\WINDOWS\System32\advapi32.dll
ModLoad: 00007ffb610d0000 00007ffb6112b000
C:\WINDOWS\System32\sechost.dll ModLoad: 00007ffb57b30000
00007ffb57bc6000 C:\WINDOWS\System32\TextInputFramework.dll
(3d80.256c): Break instruction exception - code 80000003 (first
chance) ntdll!LdrpDoDebuggerBreak+0x30: 00007ffb`6294c93c cc
int 3

Well, the weirdness you are seeing around 64 byte transfers is well known:
Delimiting write transfers with short packets
The USB driver stack
driver does not impose the same restrictions on packet size, when
writing to the device, that it imposes when reading from the device.
Some client drivers must make frequent transmissions of small
quantities of control data to manage their devices. It is impractical
to restrict data transmissions to packets of uniform size in such
cases. Therefore, the driver stack does not assign any special
significance to packets of size less than the endpoint's maximum size
during data writes. This allows a client driver to break a large
transfer to the device into multiple URBs of any size less than or
equal to the maximum.
The driver must either end the transmission by means of a packet of
less than maximum size, or delimit the end of the transmission by
means of a zero-length packet. The transmission is not complete until
the driver sends a packet smaller than wMaxPacketSize. If the transfer
size is an exact multiple of the maximum, the driver must send a
zero-length delimiting packet to explicitly terminate the transfer
Delimiting the data transmission with zero-length packets, as required
by the USB specification, is the responsibility of the client driver.
The USB driver stack does not generate these packets automatically.
From USB Transfer and Packet Sizes on MSDN
As for the rest... depending on whether the device declares itself to be a composite device or not, Windows's driver loader will pick either one or multiple device drivers to connect to the device. Those drivers handle choosing which endpoint on the device to talk to. So from userspace, it is normally enough to open the driver interface and begin doing I/O. The HID class driver, for example, knows how to identify and enable the HID endpoint.
Since you have things working with UWP, it seems likely that you have the WinUSB driver loaded (since that's Step #1). Therefore you'll use the WinUSB API to talk to it.
Here's the documentation for the C and C++ API for WinUSB. It has examples of endpoint setup, and looks quite a bit less messy than the libusb code you quoted (although that may have to do with code formatting and style as well).

Here's how to connect to and read/write to/from a USB device via the WinUSB library
All this code is contained in the Device.Net repo: https://github.com/MelbourneDeveloper/Device.Net . There is a sample here. It automatically switched between Hid, and UWP depending on which device is plugged in. https://github.com/MelbourneDeveloper/Device.Net/blob/master/src/Usb.Net.WindowsSample/Program.cs
Connect and get Information
Call SetupDiGetClassDevs to enumerate devices with the WinUSB Guid (dee824ef-729b-4a0e-9c14-b7117d33a817). Code: https://github.com/MelbourneDeveloper/Device.Net/blob/58aca0de118576ba89ec7437b29176d9bdd90aea/src/Device.Net/Windows/WindowsDeviceFactoryBase.cs#L35
Call SetupDiGetDeviceInterfaceDetail to get details about the interface and filter by Vid/Pid. Code: https://github.com/MelbourneDeveloper/Device.Net/blob/58aca0de118576ba89ec7437b29176d9bdd90aea/src/Device.Net/Windows/WindowsDeviceFactoryBase.cs#L64
Call CreateFile with the returned Device Path. Code: https://github.com/MelbourneDeveloper/Device.Net/blob/58aca0de118576ba89ec7437b29176d9bdd90aea/src/Usb.Net/Windows/WindowsUsbDevice.cs#L44
Call WinUsb_Initialize with the handle you got from the previous call. Code: https://github.com/MelbourneDeveloper/Device.Net/blob/58aca0de118576ba89ec7437b29176d9bdd90aea/src/Usb.Net/Windows/WindowsUsbDevice.cs#L53
Call WinUsb_GetDescriptor to get information about the device. Code: https://github.com/MelbourneDeveloper/Device.Net/blob/58aca0de118576ba89ec7437b29176d9bdd90aea/src/Usb.Net/Windows/WindowsUsbDevice.cs#L57
Call WinUsb_QueryInterfaceSettings to get information about the interfaces belonging to the USB device. Code: https://github.com/MelbourneDeveloper/Device.Net/blob/58aca0de118576ba89ec7437b29176d9bdd90aea/src/Usb.Net/Windows/WindowsUsbDevice.cs#L141
Call WinUsb_QueryPipe for each pipe that belongs to the interface. Code: https://github.com/MelbourneDeveloper/Device.Net/blob/58aca0de118576ba89ec7437b29176d9bdd90aea/src/Usb.Net/Windows/WindowsUsbDevice.cs#L148
Call WinUsb_GetAssociatedInterface to get other interfaces other than the default. This will probably not be necessary because you will already have the default interface handle from WinUsb_Initialize. Code: https://github.com/MelbourneDeveloper/Device.Net/blob/58aca0de118576ba89ec7437b29176d9bdd90aea/src/Usb.Net/Windows/WindowsUsbDevice.cs#L69
Write and Read
Call WinUsb_WritePipe to write an array of data. Code: https://github.com/MelbourneDeveloper/Device.Net/blob/58aca0de118576ba89ec7437b29176d9bdd90aea/src/Usb.Net/Windows/WindowsUsbDevice.cs#L115
Call WinUsb_ReadPipe to read an array of data. Code: https://github.com/MelbourneDeveloper/Device.Net/blob/58aca0de118576ba89ec7437b29176d9bdd90aea/src/Usb.Net/Windows/WindowsUsbDevice.cs#L98
API Calls
public static class Kernel32APICalls
{
//Abridged
#region Kernel32
[DllImport("kernel32.dll", SetLastError = true)]
public static extern SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);
#endregion
}
public static partial class WinUsbApiCalls
{
public const uint DEVICE_SPEED = 1;
public const byte USB_ENDPOINT_DIRECTION_MASK = 0X80;
public const int WritePipeId = 0x80;
/// <summary>
/// Not sure where this constant is defined...
/// </summary>
public const int DEFAULT_DESCRIPTOR_TYPE = 0x01;
[DllImport("winusb.dll", SetLastError = true)]
public static extern bool WinUsb_ControlTransfer(IntPtr InterfaceHandle, WINUSB_SETUP_PACKET SetupPacket, byte[] Buffer, uint BufferLength, ref uint LengthTransferred, IntPtr Overlapped);
[DllImport("winusb.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool WinUsb_GetAssociatedInterface(SafeFileHandle InterfaceHandle, byte AssociatedInterfaceIndex, out SafeFileHandle AssociatedInterfaceHandle);
[DllImport("winusb.dll", SetLastError = true)]
public static extern bool WinUsb_GetDescriptor(SafeFileHandle InterfaceHandle, byte DescriptorType, byte Index, ushort LanguageID, out USB_DEVICE_DESCRIPTOR deviceDesc, uint BufferLength, out uint LengthTransfered);
[DllImport("winusb.dll", SetLastError = true)]
public static extern bool WinUsb_Free(SafeFileHandle InterfaceHandle);
[DllImport("winusb.dll", SetLastError = true)]
public static extern bool WinUsb_Initialize(SafeFileHandle DeviceHandle, out SafeFileHandle InterfaceHandle);
[DllImport("winusb.dll", SetLastError = true)]
public static extern bool WinUsb_QueryDeviceInformation(IntPtr InterfaceHandle, uint InformationType, ref uint BufferLength, ref byte Buffer);
[DllImport("winusb.dll", SetLastError = true)]
public static extern bool WinUsb_QueryInterfaceSettings(SafeFileHandle InterfaceHandle, byte AlternateInterfaceNumber, out USB_INTERFACE_DESCRIPTOR UsbAltInterfaceDescriptor);
[DllImport("winusb.dll", SetLastError = true)]
public static extern bool WinUsb_QueryPipe(SafeFileHandle InterfaceHandle, byte AlternateInterfaceNumber, byte PipeIndex, out WINUSB_PIPE_INFORMATION PipeInformation);
[DllImport("winusb.dll", SetLastError = true)]
public static extern bool WinUsb_ReadPipe(SafeFileHandle InterfaceHandle, byte PipeID, byte[] Buffer, uint BufferLength, out uint LengthTransferred, IntPtr Overlapped);
[DllImport("winusb.dll", SetLastError = true)]
public static extern bool WinUsb_SetPipePolicy(IntPtr InterfaceHandle, byte PipeID, uint PolicyType, uint ValueLength, ref uint Value);
[DllImport("winusb.dll", SetLastError = true)]
public static extern bool WinUsb_WritePipe(SafeFileHandle InterfaceHandle, byte PipeID, byte[] Buffer, uint BufferLength, out uint LengthTransferred, IntPtr Overlapped);
}
Special thanks to #benvoigt for keeping me moving on this problem.

Related

Process not suspending (NtSuspendProcess) -> What is going on?

I have adapted a 32-bit memory scanner in C# to 64-bit. Before scanning a program (read: live program), it is supposed to suspend the process. I am using NtSuspendProcess at this point to attempt to achieve this end. The funny thing is, I have this program (do not have access to the source) which, once a file is opened, refuses to suspend. Figuring it was a bug in my scanner, I tried suspending the program, once again when a file is opened, using Process Explorer; it flashes "Suspended" very briefly; I looked under Suspend Count for the threads associated with the process, and it increments just fine when I tell Process Explorer to suspend it...but when I tell Process Explorer to resume the process, it increments the Suspend Count (you read that right, it INCREMENTS it).
So yes, with my memory scanner and Process Explorer, when this program has no files open, it suspends and resumes normally. When the program has a file open, it fails to suspend, and increments the Suspend Count on attempts to resume.
I suspect a number of things here. Somehow, the message to suspend is being duplicated, and being released when resume is called. This explains the Suspend Count incrementing when it should be decrementing. Which might mean the original Suspend message isn't being consumed properly.
How do I even go about debugging this problem from this point? Where do I start?
Below are some code snippets from my memory scanner:
const uint PROCESS_SUSPEND_RESUME = 0x0800;
const uint PROCESS_QUERY_INFORMATION = 0x0400;
const uint MEM_COMMIT = 0x00001000;
const uint PAGE_READWRITE = 0x04;
const uint PROCESS_WM_READ = 0x0010;
[DllImport("ntdll.dll", EntryPoint = "NtSuspendProcess", SetLastError = true, ExactSpelling = false)]
private static extern UIntPtr NtSuspendProcess(UIntPtr processHandle);
[DllImport("ntdll.dll", EntryPoint = "NtResumeProcess", SetLastError = true, ExactSpelling = false)]
private static extern UIntPtr NtResumeProcess(UIntPtr processHandle);
[DllImport("kernel32.dll")]
public static extern UIntPtr OpenProcess(UIntPtr dwDesiredAccess, bool bInheritHandle, UIntPtr dwProcessId);
[DllImport("kernel32.dll")]
public static extern bool ReadProcessMemory(UIntPtr hProcess, UIntPtr lpBaseAddress, byte[] lpBuffer, UIntPtr dwSize, out UIntPtr lpNumberOfBytesRead);
[DllImport("kernel32.dll")]
static extern void GetSystemInfo(out SYSTEM_INFO lpSystemInfo);
[DllImport("kernel32.dll", SetLastError = true)]
static extern UIntPtr VirtualQueryEx(UIntPtr hProcess, UIntPtr lpAddress, out MEMORY_BASIC_INFORMATION64 lpBuffer, UIntPtr dwLength);
[DllImport("kernel32.dll")]
static extern bool CloseHandle(UIntPtr hObject);
private void Button_Extract_Click(object sender, EventArgs e)
{
Process process = Process.GetProcessById(int.Parse(DataGridView_Processes.SelectedRows[0].Cells["Process ID"].Value.ToString()));
UIntPtr processSuspendResumeHandle = OpenProcess(new UIntPtr(PROCESS_SUSPEND_RESUME), false, new UIntPtr((uint)process.Id));
//process.Suspend();
UIntPtr suspendreturnvalue = NtSuspendProcess(processSuspendResumeHandle);
System.Diagnostics.Debug.WriteLine("Return Value: " + suspendreturnvalue.ToString());
UIntPtr processHandle = OpenProcess(new UIntPtr(PROCESS_QUERY_INFORMATION | PROCESS_WM_READ), false, new UIntPtr((uint)process.Id));
//int error = Marshal.GetLastWin32Error();
//System.Diagnostics.Debug.WriteLine("Last Win32 Error: " + error);
SYSTEM_INFO sys_info = new SYSTEM_INFO();
GetSystemInfo(out sys_info);
UIntPtr proc_min_address = sys_info.minimumApplicationAddress;
UIntPtr proc_max_address = sys_info.maximumApplicationAddress;
ulong proc_min_address_l = (ulong)proc_min_address;
ulong proc_max_address_l = (ulong)proc_max_address;
//Skip to end
CloseHandle(processHandle);
NtResumeProcess(processSuspendResumeHandle);
CloseHandle(processSuspendResumeHandle);
MessageBox.Show("Extraction Complete.");
}
Do the following:
Make sure you are running as an admin
Check the return code of OpenProcess it should return a nonzero, if not continue to step 3.
Check the return code of GetLastError, search its value here https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499- (this only contains error 0-400 so check the sidebar, there's a link to the other pages that contain other error codes)
Check the returned value of NtSuspendProcess and search it on the link above.
Since it looks like you're accessing the memory, you're probably using this app for game cheats.... so check OpenProcess, NtOpenProcess, NtSuspendProcess, and NtResumeProcess using an assembler such as Cheat Engine or x64dbg.
Go to the address of the functions I highlighted. Check if the first 5 bytes have a JMP instruction. If it has then that means the function has been patched by either an antivirus or an anticheat.
If it's patched, you might wanna disable the antivirus and if its still patched then you're against an anticheat. Switch to C++, use direct syscalls using syswhispers2, use Qt for C++ its a much better alternative to C# Winforms, it has a drag-and-drop form builder too.
If there is nothing suspicious, the functions didn't look patched. The GetLastError calls return normal status. Then you might be against a rootkit anticheat. In that case, you'll have to write your own driver to handle the open process, read memory, and write memory using C++ and WDK. You'll also need to find a mapper for your unsigned driver (KDMapper, LPMapper, etc) since it's not possible to load unsigned driver in the OS without disabling Driver Signature Enforcement feature of Windows, which when disabled, can be detected by an anti cheat.
EDIT:
I found this article about NtSuspendProcess failing when opening a process with PROCESS_SUSPEND_RESUME access mask. Try using PROCESS_ALL_ACCESS access mask instead.
https://social.msdn.microsoft.com/Forums/vstudio/en-US/e119f8e8-98bc-466e-a9bb-88bcbde6524c/why-does-ntsuspendprocess-fail-with-processsuspendresume-but-succeeds-with-processallaccess?forum=windowssdk

Calling FreeLibraryAndExitThread externally for a remote process

I'm to trying to call FreeLibraryAndExitThread externally in another process (using CreateRemoteThread) so that I can unload a module I loaded in externally through LoadLibrary.
I understand that whilst CreateRemoteThread takes 1 parameter, you can provide it with a struct of multiple arguments if you need more than one.
If have tried the following which did not unload the module. In fact it seemed to do nothing.
Note I have removed all error checking to keep this post simple and short
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern IntPtr GetModuleHandle(string moduleName);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern IntPtr GetProcAddress(IntPtr moduleHandle, string procName);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern IntPtr VirtualAllocEx(IntPtr processHandle, IntPtr baseAddress, int size, int allocationType, int protection);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool WriteProcessMemory(IntPtr processHandle, IntPtr baseAddress, byte[] buffer, int size, int bytesWritten);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern IntPtr CreateRemoteThread(IntPtr processHandle, IntPtr threadAttributes, int stackSize, IntPtr startAddress, IntPtr parameter, int creationFlags, int threadId);
private struct FreeLibraryAndExitThreadParameters
{
internal IntPtr ModuleAddress;
internal int ExitCode;
}
var process = Process.GetProcessesByName("notepad")[0];
var freeLibraryAndExitThreadAddress = GetProcAddress(GetModuleHandle("kernel32.dll"), "FreeLibraryAndExitThread");
// Get an instance of the module - dllName is the name of the module I am trying to unload
var module = process.Modules.Cast<ProcessModule>().SingleOrDefault(m => string.Equals(m.ModuleName, dllName, StringComparison.OrdinalIgnoreCase));
var freeLibraryAndExitThreadParameters = new FreeLibraryAndExitThreadParameters { ModuleAddress = module.BaseAddress, ExitCode = 0 };
// This code turns the struct into a byte array
var structureSize = Marshal.SizeOf(freeLibraryAndExitThreadParameters);
var structureBytes = new byte[structureSize];
var buffer = Marshal.AllocHGlobal(structureSize);
Marshal.StructureToPtr(freeLibraryAndExitThreadParameters, buffer, true);
Marshal.Copy(buffer, structureBytes, 0, structureSize);
Marshal.FreeHGlobal(buffer);
// Allocate memory in the remote process with commit and reserve allocation type and PageExecuteReadWrite permissions
var remoteAddress = VirtualAllocEx(process.Handle, IntPtr.Zero, structureSize, 0x01000 | 0x02000, 0x040);
// Write the structure into the remote process
WriteProcessMemory(process.Handle, remoteAddress, buffer, structureSize, 0);
// Finally call CreateRemoteThread to execute the function in the remote process
CreateRemoteThread(process.Handle, IntPtr.Zero, 0, freeLibraryAndExitThreadAddress, remoteAddress, 0, 0);
None of the pinvoke calls are actually failing and I can see that the bytes are being written into memory but nothing seems to happen after the remote thread is created - In my actual code I call WaitForSingleObject and the thread finishes its task also with no problem.
Can someone point out what I'm doing wrong and how I can fix this problem so that I can externally call FreeLibraryAndExitThread in a remote process?
It may be worth mentioning that I can use FreeLibrary with this method - it works fine(removing the struct as it only takes 1 parameter) but I specifically need to use FreeLibraryAndExitThread for the module I need to unload which is why I am not using the simpler FreeLibrary.
formally this is simply, all what we need
CreateRemoteThread(hProcess, 0, 0, (PTHREAD_START_ROUTINE)FreeLibraryAndExitThread, hmod, 0, 0)
where hmod is address of module in remote process. address of FreeLibraryAndExitThread can be take from current process kernel32!FreeLibraryAndExitThread - until kernel32.dll is loaded at the same base address in all processes.
that
DECLSPEC_NORETURN
VOID
WINAPI
FreeLibraryAndExitThread(
_In_ HMODULE hLibModule,
_In_ DWORD dwExitCode
);
take 2 parameters in concrete case no problem. as result of call - CreateRemoteThread(hProcess, 0, 0, (PTHREAD_START_ROUTINE)FreeLibraryAndExitThread, hmod, 0, 0) the FreeLibraryAndExitThread will be called via stdcall (WINAPI) calling convention with single parameter - hmod. the second parameter dwExitCode will be undefined in this case, but it not play any role - any return code of thread is ok. system not interpret this value. and because this concrete api never return - different in parameter count also not play role.
another question - for what, which sense unload module in remote process. and if module really will be unloaded (the FreeLibrary call only decrement module load count, so module not always will be unloaded during this call) and after this some code in remote process call code of unloading module - think not need explain what is be in this case

WinUSB_Initialize function occurs INVALID_FUNCTION (0x1) Error

Why does the WinUSB_Initialize function occur INVALID_FUNCTION (0x1) Error?
This is a function from the winusb.dll which returns an interface handle.
I would like to get an Interface handle.
I already have a Device handle.
internal struct devInfo
{
internal SafeFileHandle deviceHandle;
internal IntPtr winUsbHandle;
internal Byte bulkInPipe;
internal Byte bulkOutPipe;
internal Byte interruptInPipe;
internal Byte interruptOutPipe;
internal UInt32 devicespeed;
}
internal const Int32 FILE_ATTRIBUTE_NORMAL = 0X80;
internal const Int32 FILE_FLAG_OVERLAPPED = 0X40000000;
internal const Int32 FILE_SHARE_READ = 1;
internal const Int32 FILE_SHARE_WRITE = 2;
internal const UInt32 GENERIC_READ = 0X80000000;
internal const UInt32 GENERIC_WRITE = 0X40000000;
internal const Int32 OPEN_EXISTING = 3;
[DllImport("winusb.dll", SetLastError = true)]
internal static extern Boolean WinUsb_Initialize
(SafeFileHandle DeviceHandle,
ref IntPtr InterfaceHandle);
internal devInfo myDevInfo; // = new devInfo();
public IntPtr Get_WinUSB_handle()
{
Guid myGuid = Get_HID_GUID();
IntPtr deviceInfoSet = Get_Device_Info_Set(myGuid);
SP_DEVICE_INTERFACE_DATA MyDeviceInterfaeData = Get_Device_Interface_Data(deviceInfoSet, myGuid);
IntPtr detailDataBuffer = Get_Structure_with_Device_PathName(deviceInfoSet, ref MyDeviceInterfaeData);
string devicePathName = Get_Device_PathName(detailDataBuffer);
myDevInfo.deviceHandle= CreateFile(devicePathName,
(GENERIC_WRITE | GENERIC_READ),
FILE_SHARE_READ | FILE_SHARE_WRITE,
IntPtr.Zero,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
0);
Boolean success;
success = WinUsb_Initialize(myDevInfo.deviceHandle, ref myDevInfo.winUsbHandle);
System.Console.WriteLine(Marshal.GetLastWin32Error());
System.Console.WriteLine(success);
return myDevInfo.winUsbHandle;
}
success = WinUsb_Initialize(...);
System.Console.WriteLine(Marshal.GetLastWin32Error());
This kind of error checking is wrong. It is only valid to call Marshal.GetLastWin32Error() when a winapi function failed. So a rock-hard requirement is to check success first. Calling GetLastWin32Error() anyway produces an arbitrary garbage value if the function actually succeeded. ERROR_INVALID_FUNCTION certainly has a high garbage value.
The code is also fairly broken when there actually is an error, it doesn't nearly make enough noise and the client code can easily ignore the invalid handle value. Proper code is:
bool success = WinUsb_Initialize(...);
if (!success) throw new System.ComponentModel.Win32Exception();
The exception constructor already calls Marshal.GetLastWin32Error() and will produce an appropriate localized error message.
I believe you are connecting to a device that is not actually using winusb.sys as one of its drivers. To other people who might read this, you can check if a device uses winusb.sys by double-clicking it in the Device Manager, going to the "Driver" tab, and clicking on "Driver Details". If you don't see winusb.sys there then this is not a WinUSB device.
To have a WinUSB device, you need to write a proper INF file and then tell Windows to use it one way or another.
It looks like you are trying to access an HID. Instead of using WinUSB I would recommend HIDAPI.

SerialPort port.open "The port 'COM2' does not exist."

I'm having a big problem with the SerialPort.Open();
I am communicating with an usb virtual com port (cdc), and it is listed as COM2.
It works fine in TeraTerm/hyperTerminal ect. but when I try to open the port in C#, it gives me the Exception The port 'COM2' does not exist.
I get the port name using the SerialPort.GetPortNames() function, and it looks fine when I debug.
I have tried to set the name hardcoded, but with no luck.
Now the really strange thing, it works fine on some PC's, and fails on other PC's. On some PC's it fail all the time, and on others it fails 10% of the time.
Even more strange it depends on the usb port used, some ports works fine, others fail (on the same PC!).
Could anybody help me please?
I've worked with virtual serial ports before. Oftentimes they aren't accessible as "COMX:" to some windows API calls, and you have to fully specify them. That might be the case here. Try using the Windows device namespace path for the serial device. For example: "\\.\COM2"
Another thing I've found useful for debugging is opening up a Hyperterm on the given serial port.
One final thing: For debugging your logic on systems that don't have all the hardware, I found this wonderful program called com0com. It is a GPL Sourceforge project that creates tied pairs of virtual com ports on your system. Whatever is written to one can be read from the other, and visa versa. You can either write an emulator and give it one of the ports, or just open up Hyperterm on it. Then give the other to your program. Testing with no cables or other hardware required.
This error can be caused if the driver returns an unexpected "file type" for "COM2".
Try p/Invoking GetFileType and I believe you'll see the pattern. It has to be FILE_TYPE_CHAR or FILE_TYPE_UNKNOWN or else SerialPort will throw that exception.
class Program
{
static void Main(string[] args)
{
string portName = #"COM2";
IntPtr handle = CreateFile(portName, 0, 0, IntPtr.Zero, 3, 0x80, IntPtr.Zero);
if (handle == (IntPtr)(-1))
{
Console.WriteLine("Could not open " + portName + ": " + new Win32Exception().Message);
Console.ReadKey();
return;
}
FileType type = GetFileType(handle);
Console.WriteLine("File " + portName + " reports its type as: " + type);
Console.ReadKey();
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
public static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr SecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);
[DllImport("kernel32.dll")]
static extern FileType GetFileType(IntPtr hFile);
enum FileType : uint
{
UNKNOWN = 0x0000,
DISK = 0x0001,
CHAR = 0x0002,
PIPE = 0x0003,
REMOTE = 0x8000,
}
}
Also see this thread on MSDN forums.

How to read and modify NTFS Alternate Data Streams using .NET [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
How can I read and modify "NTFS Alternate Data Streams" using .NET?
It seems there is no native .NET support for it. Which Win32 API's would I use? Also, how would I use them, as I don't think this is documented?
Here is a version for C#
using System.Runtime.InteropServices;
class Program
{
static void Main(string[] args)
{
var mainStream = NativeMethods.CreateFileW(
"testfile",
NativeConstants.GENERIC_WRITE,
NativeConstants.FILE_SHARE_WRITE,
IntPtr.Zero,
NativeConstants.OPEN_ALWAYS,
0,
IntPtr.Zero);
var stream = NativeMethods.CreateFileW(
"testfile:stream",
NativeConstants.GENERIC_WRITE,
NativeConstants.FILE_SHARE_WRITE,
IntPtr.Zero,
NativeConstants.OPEN_ALWAYS,
0,
IntPtr.Zero);
}
}
public partial class NativeMethods
{
/// Return Type: HANDLE->void*
///lpFileName: LPCWSTR->WCHAR*
///dwDesiredAccess: DWORD->unsigned int
///dwShareMode: DWORD->unsigned int
///lpSecurityAttributes: LPSECURITY_ATTRIBUTES->_SECURITY_ATTRIBUTES*
///dwCreationDisposition: DWORD->unsigned int
///dwFlagsAndAttributes: DWORD->unsigned int
///hTemplateFile: HANDLE->void*
[DllImportAttribute("kernel32.dll", EntryPoint = "CreateFileW")]
public static extern System.IntPtr CreateFileW(
[InAttribute()] [MarshalAsAttribute(UnmanagedType.LPWStr)] string lpFileName,
uint dwDesiredAccess,
uint dwShareMode,
[InAttribute()] System.IntPtr lpSecurityAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
[InAttribute()] System.IntPtr hTemplateFile
);
}
public partial class NativeConstants
{
/// GENERIC_WRITE -> (0x40000000L)
public const int GENERIC_WRITE = 1073741824;
/// FILE_SHARE_DELETE -> 0x00000004
public const int FILE_SHARE_DELETE = 4;
/// FILE_SHARE_WRITE -> 0x00000002
public const int FILE_SHARE_WRITE = 2;
/// FILE_SHARE_READ -> 0x00000001
public const int FILE_SHARE_READ = 1;
/// OPEN_ALWAYS -> 4
public const int OPEN_ALWAYS = 4;
}
A First, nothing in the Microsoft® .NET Framework provides this functionality. If you want it, plain and simple you'll need to do some sort of interop, either directly or using a third-party library.
If you're using Windows Server™ 2003 or later, Kernel32.dll exposes counterparts to FindFirstFile and FindNextFile that provide the exact functionality you're looking for. FindFirstStreamW and FindNextStreamW allow you to find and enumerate all of the Alternate Data Streams within a particular file, retrieving information about each, including its name and its length. The code for using these functions from managed code is very similar to that which I showed in my December column, and is shown in Figure 1.
Figure 1 Using FindFirstStreamW and FindNextStreamW
[SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
public sealed class SafeFindHandle : SafeHandleZeroOrMinusOneIsInvalid {
private SafeFindHandle() : base(true) { }
protected override bool ReleaseHandle() {
return FindClose(this.handle);
}
[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
private static extern bool FindClose(IntPtr handle);
}
public class FileStreamSearcher {
private const int ERROR_HANDLE_EOF = 38;
private enum StreamInfoLevels { FindStreamInfoStandard = 0 }
[DllImport("kernel32.dll", ExactSpelling = true, CharSet = CharSet.Auto, SetLastError = true)]
private static extern SafeFindHandle FindFirstStreamW(string lpFileName, StreamInfoLevels InfoLevel, [In, Out, MarshalAs(UnmanagedType.LPStruct)] WIN32_FIND_STREAM_DATA lpFindStreamData, uint dwFlags);
[DllImport("kernel32.dll", ExactSpelling = true, CharSet = CharSet.Auto, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool FindNextStreamW(SafeFindHandle hndFindFile, [In, Out, MarshalAs(UnmanagedType.LPStruct)] WIN32_FIND_STREAM_DATA lpFindStreamData);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private class WIN32_FIND_STREAM_DATA {
public long StreamSize;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 296)]
public string cStreamName;
}
public static IEnumerable<string> GetStreams(FileInfo file) {
if (file == null) throw new ArgumentNullException("file");
WIN32_FIND_STREAM_DATA findStreamData = new WIN32_FIND_STREAM_DATA();
SafeFindHandle handle = FindFirstStreamW(file.FullName, StreamInfoLevels.FindStreamInfoStandard, findStreamData, 0);
if (handle.IsInvalid) throw new Win32Exception();
try {
do {
yield return findStreamData.cStreamName;
} while (FindNextStreamW(handle, findStreamData));
int lastError = Marshal.GetLastWin32Error();
if (lastError != ERROR_HANDLE_EOF) throw new Win32Exception(lastError);
} finally {
handle.Dispose();
}
}
}
You simply call FindFirstStreamW, passing to it the full path to the target file. The second parameter to FindFirstStreamW dictates the level of detail you want in the returned data; currently, there is only one level (FindStreamInfoStandard), which has a numerical value of 0. The third parameter to the function is a pointer to a WIN32_FIND_STREAM_DATA structure (technically, what the third parameter points to is dictated by the value of the second parameter detailing the information level, but as there's currently only one level, for all intents and purposes this is a WIN32_FIND_STREAM_DATA). I've declared that structure's managed counterpart as a class, and in the interop signature I've marked it to be marshaled as a pointer to a struct. The last parameter is reserved for future use and should be 0.
If a valid handle is returned from FindFirstStreamW, the WIN32_FIND_STREAM_DATA instance contains information about the stream found, and its cStreamName value can be yielded back to the caller as the first stream name available. FindNextStreamW accepts the handle returned from FindFirstStreamW and fills the supplied WIN32_FIND_STREAM_DATA with information about the next stream available, if it exists. FindNextStreamW returns true if another stream is available, or false if not.
As a result, I continually call FindNextStreamW and yield the resulting stream name until FindNextStreamW returns false. When that happens, I double check the last error value to make sure that the iteration stopped because FindNextStreamW ran out of streams, and not for some unexpected reason.
Unfortunately, if you're using Windows® XP or Windows 2000 Server, these functions aren't available to you, but there are a couple of alternatives. The first solution involves an undocumented function currently exported from Kernel32.dll, NTQueryInformationFile. However, undocumented functions are undocumented for a reason, and they can be changed or even removed at any time in the future. It's best not to use them. If you do want to use this function, search the Web and you'll find plenty of references and sample source code. But do so at your own risk.
Another solution, and one which I've demonstrated in Figure 2, relies on two functions exported from Kernel32.dll, and these are documented. As their names imply, BackupRead and BackupSeek are part of the Win32® API for backup support:
BOOL BackupRead(HANDLE hFile, LPBYTE lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, BOOL bAbort, BOOL bProcessSecurity, LPVOID* lpContext);
BOOL BackupSeek(HANDLE hFile, DWORD dwLowBytesToSeek, DWORD dwHighBytesToSeek, LPDWORD lpdwLowByteSeeked, LPDWORD lpdwHighByteSeeked, LPVOID* lpContext);
Figure 2 Using BackupRead and BackupSeek
public enum StreamType {
Data = 1,
ExternalData = 2,
SecurityData = 3,
AlternateData = 4,
Link = 5,
PropertyData = 6,
ObjectID = 7,
ReparseData = 8,
SparseDock = 9
}
public struct StreamInfo {
public StreamInfo(string name, StreamType type, long size) {
Name = name;
Type = type;
Size = size;
}
readonly string Name;
public readonly StreamType Type;
public readonly long Size;
}
public class FileStreamSearcher {
[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool BackupRead(SafeFileHandle hFile, IntPtr lpBuffer, uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, [MarshalAs(UnmanagedType.Bool)] bool bAbort, [MarshalAs(UnmanagedType.Bool)] bool bProcessSecurity, ref IntPtr lpContext);[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool BackupSeek(SafeFileHandle hFile, uint dwLowBytesToSeek, uint dwHighBytesToSeek, out uint lpdwLowByteSeeked, out uint lpdwHighByteSeeked, ref IntPtr lpContext); public static IEnumerable<StreamInfo> GetStreams(FileInfo file) {
const int bufferSize = 4096;
using (FileStream fs = file.OpenRead()) {
IntPtr context = IntPtr.Zero;
IntPtr buffer = Marshal.AllocHGlobal(bufferSize);
try {
while (true) {
uint numRead;
if (!BackupRead(fs.SafeFileHandle, buffer, (uint)Marshal.SizeOf(typeof(Win32StreamID)), out numRead, false, true, ref context)) throw new Win32Exception();
if (numRead > 0) {
Win32StreamID streamID = (Win32StreamID)Marshal.PtrToStructure(buffer, typeof(Win32StreamID));
string name = null;
if (streamID.dwStreamNameSize > 0) {
if (!BackupRead(fs.SafeFileHandle, buffer, (uint)Math.Min(bufferSize, streamID.dwStreamNameSize), out numRead, false, true, ref context)) throw new Win32Exception(); name = Marshal.PtrToStringUni(buffer, (int)numRead / 2);
}
yield return new StreamInfo(name, streamID.dwStreamId, streamID.Size);
if (streamID.Size > 0) {
uint lo, hi; BackupSeek(fs.SafeFileHandle, uint.MaxValue, int.MaxValue, out lo, out hi, ref context);
}
} else break;
}
} finally {
Marshal.FreeHGlobal(buffer);
uint numRead;
if (!BackupRead(fs.SafeFileHandle, IntPtr.Zero, 0, out numRead, true, false, ref context)) throw new Win32Exception();
}
}
}
}
The idea behind BackupRead is that it can be used to read data from a file into a buffer, which can then be written to the backup storage medium. However, BackupRead is also very handy for finding out information about each of the Alternate Data Streams that make up the target file. It processes all of the data in the file as a series of discrete byte streams (each Alternate Data Stream is one of these byte streams), and each of the streams is preceded by a WIN32_STREAM_ID structure. Thus, in order to enumerate all of the streams, you simply need to read through all of these WIN32_STREAM_ID structures from the beginning of each stream (this is where BackupSeek becomes very handy, as it can be used to jump from stream to stream without having to read through all of the data in the file).
To begin, you first need to create a managed counterpart for the unmanaged WIN32_STREAM_ID structure:
typedef struct _WIN32_STREAM_ID {
DWORD dwStreamId; DWORD dwStreamAttributes;
LARGE_INTEGER Size;
DWORD dwStreamNameSize;
WCHAR cStreamName[ANYSIZE_ARRAY];
} WIN32_STREAM_ID;
For the most part, this is like any other structure you'd marshal through P/Invoke. However, there are a few complications. First and foremost, WIN32_STREAM_ID is a variable-sized structure. Its last member, cStreamName, is an array with length ANYSIZE_ARRAY. While ANYSIZE_ARRAY is defined to be 1, cStreamName is just the address of the rest of the data in the structure after the previous four fields, which means that if the structure is allocated to be larger than sizeof (WIN32_STREAM_ID) bytes, that extra space will in effect be part of the cStreamName array. The previous field, dwStreamNameSize, specifies exactly how long the array is.
While this is great for Win32 development, it wreaks havoc on a marshaler that needs to copy this data from unmanaged memory to managed memory as part of the interop call to BackupRead. How does the marshaler know how big the WIN32_STREAM_ID structure actually is, given that it's variable sized? It doesn't.
The second problem has to do with packing and alignment. Ignoring cStreamName for a moment, consider the following possibility for your managed WIN32_STREAM_ID counterpart:
[StructLayout(LayoutKind.Sequential)]
public struct Win32StreamID {
public int dwStreamId;
public int dwStreamAttributes;
public long Size;
public int dwStreamNameSize;
}
An Int32 is 4 bytes in size and an Int64 is 8 bytes. Thus, you would expect this struct to be 20 bytes. However, if you run the following code, you'll find that both values are 24, not 20:
int size1 = Marshal.SizeOf(typeof(Win32StreamID));
int size2 = sizeof(Win32StreamID); // in an unsafe context
The issue is that the compiler wants to make sure that the values within these structures are always aligned on the proper boundary. Four-byte values should be at addresses divisible by 4, 8-byte values should be at boundaries divisible by 8, and so on. Now imagine what would happen if you were to create an array of Win32StreamID structures. All of the fields in the first instance of the array would be properly aligned. For example, since the Size field follows two 32-bit integers, it would be 8 bytes from the start of the array, perfect for an 8-byte value. However, if the structure were 20-bytes in size, the second instance in the array would not have all of its members properly aligned. The integer values would all be fine, but the long value would be 28 bytes from the start of the array, a value not evenly divisible by 8. To fix this, the compiler pads the structure to a size of 24, such that all of the fields will always be properly aligned (assuming the array itself is).
If the compiler's doing the right thing, you might be wondering why I'm concerned about this. You'll see why if you look at the code in Figure 2. In order to get around the first marshaling issue I described, I do in fact leave the cStreamName out of the Win32StreamID structure. I use BackupRead to read in enough bytes to fill my Win32StreamID structure, and then I examine the structure's dwStreamNameSize field. Now that I know how long the name is, I can use BackupRead again to read in the string's value from the file. That's all well and dandy, but if Marshal.SizeOf returns 24 for my Win32StreamID structure instead of 20, I'll be attempting to read too much data.
To avoid this, I need to make sure that the size of Win32StreamID is in fact 20 and not 24. This can be accomplished in two different ways using fields on the StructLayoutAttribute that adorns the structure. The first is to use the Size field, which dictates to the runtime exactly how big the structure should be:
[StructLayout(LayoutKind.Sequential, Size = 20)]
The second option is to use the Pack field. Pack indicates the packing size that should be used when the LayoutKind.Sequential value is specified and controls the alignment of the fields within the structure. The default packing size for a managed structure is 8. If I change that to 4, I get the 20-byte structure I'm looking for (and as I'm not actually using this in an array, I don't lose efficiency or stability that might result from such a packing change):
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct Win32StreamID {
public StreamType dwStreamId;
public int dwStreamAttributes;
public long Size;
public int dwStreamNameSize; // WCHAR cStreamName[1];
}
With this code in place, I can now enumerate all of the streams in a file, as shown here:
static void Main(string[] args) {
foreach (string path in args) {
Console.WriteLine(path + ":");
foreach (StreamInfo stream in FileStreamSearcher.GetStreams(new FileInfo(path))) {
Console.WriteLine("\t{0}\t{1}\t{2}", stream.Name != null ? stream.Name : "(unnamed)", stream.Type, stream.Size);
}
}
}
You'll notice that this version of FileStreamSearcher returns more information than the version that uses FindFirstStreamW and FindNextStreamW. BackupRead can provide data on more than just the primary stream and Alternate Data Streams, also operating on streams containing security information, reparse data, and more. If you only want to see the Alternate Data Streams, you can filter based on the StreamInfo's Type property, which will be StreamType.AlternateData for Alternate Data Streams.
To test this code, you can create a file that has Alternate Data Streams using the echo command at the command prompt:
> echo ".NET Matters" > C:\test.txt
> echo "MSDN Magazine" > C:\test.txt:magStream
> StreamEnumerator.exe C:\test.txt
test.txt:
(unnamed) SecurityData 164
(unnamed) Data 17
:magStream:$DATA AlternateData 18
> type C:\test.txt
".NET Matters"
> more < C:\test.txt:magStream
"MSDN Magazine"
So, now you're able to retrieve the names of all Alternate Data Streams stored in a file. Great. But what if you want to actually manipulate the data in one of those streams? Unfortunately, if you attempt to pass a path for an Alternate Data Stream to one of the FileStream constructors, a NotSupportedException will be thrown: "The given path's format is not supported."
To get around this, you can bypass FileStream's path canonicalization checks by directly accessing the CreateFile function exposed from kernel32.dll (see Figure 3). I've used a P/Invoke for the CreateFile function to open and retrieve a SafeFileHandle for the specified path, without performing any of the managed permission checks on the path, so it can include Alternate Data Stream identifiers. This SafeFileHandle is then used to create a new managed FileStream, providing the required access. With that in place, it's easy to manipulate the contents of an Alternate Data Stream using the System.IO namespace's functionality. The following example reads and prints out the contents of the C:\test.txt:magStream created in the previous example:
string path = #"C:\test.txt:magStream";
using (StreamReader reader = new StreamReader(CreateFileStream(path, FileAccess.Read, FileMode.Open, FileShare.Read))) {
Console.WriteLine(reader.ReadToEnd());
}
Figure 3 Using P/Invoke for CreateFile
private static FileStream CreateFileStream(string path, FileAccess access, FileMode mode, FileShare share) {
if (mode == FileMode.Append) mode = FileMode.OpenOrCreate; SafeFileHandle handle = CreateFile(path, access, share, IntPtr.Zero, mode, 0, IntPtr.Zero);
if (handle.IsInvalid) throw new IOException("Could not open file stream.", new Win32Exception());
return new FileStream(handle, access);
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern SafeFileHandle CreateFile(string lpFileName, FileAccess dwDesiredAccess, FileShare dwShareMode, IntPtr lpSecurityAttributes, FileMode dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile);
Stephen Toub in MSDN Magazine from January 2006.
There is no native .NET support for them. You have to use P/Invoke to call the native Win32 methods.
To create them, call CreateFile with a path like filename.txt:streamname. If you use the interop call that returns a SafeFileHandle, you can use that to construct a FileStream that you can then read & write to.
To list the streams that exist on a file, use FindFirstStreamW and FindNextStreamW (which exist only on Server 2003 and later - not XP).
I don't believe you can delete a stream, except by copying the rest of the file and leaving off one of the streams. Setting the length to 0 may also work, but I haven't tried it.
You can also have alternate data streams on a directory. You access them the same as with files - C:\some\directory:streamname.
Streams can have compression, encryption and sparseness set on them independent of the default stream.
This nuget package CodeFluent Runtime Client has (amongst other utilities) an NtfsAlternateStream Class that supports create/read/update/delete/enumeration operations.
Not in .NET:
http://support.microsoft.com/kb/105763
#include <windows.h>
#include <stdio.h>
void main( )
{
HANDLE hFile, hStream;
DWORD dwRet;
hFile = CreateFile( "testfile",
GENERIC_WRITE,
FILE_SHARE_WRITE,
NULL,
OPEN_ALWAYS,
0,
NULL );
if( hFile == INVALID_HANDLE_VALUE )
printf( "Cannot open testfile\n" );
else
WriteFile( hFile, "This is testfile", 16, &dwRet, NULL );
hStream = CreateFile( "testfile:stream",
GENERIC_WRITE,
FILE_SHARE_WRITE,
NULL,
OPEN_ALWAYS,
0,
NULL );
if( hStream == INVALID_HANDLE_VALUE )
printf( "Cannot open testfile:stream\n" );
else
WriteFile(hStream, "This is testfile:stream", 23, &dwRet, NULL);
}

Categories