RtlCompressBuffer API in C# - c#

I'm trying to use the RtlGetCompressionWorkSpaceSize and RtlCompressBuffer functions in a C# project.
Here is what I have so far:
class Program
{
const uint COMPRESSION_FORMAT_LZNT1 = 2;
const uint COMPRESSION_ENGINE_MAXIMUM = 0x100;
[DllImport("ntdll.dll")]
static extern uint RtlGetCompressionWorkSpaceSize(uint CompressionFormat, out uint pNeededBufferSize, out uint Unknown);
[DllImport("ntdll.dll")]
static extern uint RtlCompressBuffer(uint CompressionFormat, byte[] SourceBuffer, uint SourceBufferLength, out byte[] DestinationBuffer,
uint DestinationBufferLength, uint Unknown, out uint pDestinationSize, IntPtr WorkspaceBuffer);
static void Main(string[] args)
{
uint dwSize = 0;
uint dwRet = 0;
uint ret = RtlGetCompressionWorkSpaceSize(COMPRESSION_FORMAT_LZNT1 | COMPRESSION_ENGINE_MAXIMUM, out dwSize, out dwRet);
IntPtr pMem = Marshal.AllocHGlobal((int)dwSize);
byte[] buffer = new byte[1024];
byte[] outBuf = new byte[1024];
uint destSize = 0;
ret = RtlCompressBuffer(COMPRESSION_FORMAT_LZNT1 | COMPRESSION_ENGINE_MAXIMUM, buffer, 1024, out outBuf, 1024, 0, out destSize, pMem);
Console.Write(ret.ToString());
Console.Read();
}
}
RtlGetCompressionWorkSpaceSize works since it returns 0 (NT success code) but when I call RtlCompressBuffer I get a Memory Access Violation error.
EDIT: With help from David's answer I've fixed the issue and the correct code is below.
const ushort COMPRESSION_FORMAT_LZNT1 = 2;
const ushort COMPRESSION_ENGINE_MAXIMUM = 0x100;
[DllImport("ntdll.dll")]
static extern uint RtlGetCompressionWorkSpaceSize(ushort CompressionFormat, out uint pNeededBufferSize, out uint Unknown);
[DllImport("ntdll.dll")]
static extern uint RtlCompressBuffer(ushort CompressionFormat, byte[] SourceBuffer, int SourceBufferLength, byte[] DestinationBuffer,
int DestinationBufferLength, uint Unknown, out int pDestinationSize, IntPtr WorkspaceBuffer);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern IntPtr LocalAlloc(int uFlags, IntPtr sizetdwBytes);
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr LocalFree(IntPtr hMem);
internal static byte[] Compress(byte[] buffer)
{
var outBuf = new byte[buffer.Length * 6];
uint dwSize = 0, dwRet = 0;
uint ret = RtlGetCompressionWorkSpaceSize(COMPRESSION_FORMAT_LZNT1 | COMPRESSION_ENGINE_MAXIMUM, out dwSize, out dwRet);
if (ret != 0)
{
return null;
}
int dstSize = 0;
IntPtr hWork = LocalAlloc(0, new IntPtr(dwSize));
ret = RtlCompressBuffer(COMPRESSION_FORMAT_LZNT1 | COMPRESSION_ENGINE_MAXIMUM, buffer,
buffer.Length, outBuf, outBuf.Length, 0, out dstSize, hWork);
if (ret != 0)
{
return null;
}
LocalFree(hWork);
Array.Resize(ref outBuf, dstSize);
return outBuf;
}

You are very nearly there. The problem is this part of your P/invoke for RtlCompressBuffer:
out byte[] DestinationBuffer
The default marshalling for byte[] is for the array contents to marshalled in both directions, from managed to unmanaged, and then back again when the function returns. The C definition of RtlCompressBuffer is annotated with __out but that means that the array contents are __out rather than the pointer being __out.
Change your P/invoke to
byte[] DestinationBuffer
and similarly in the call to RtlCompressBuffer change out outBuf to outBuf and you should be good to go.
Be warned that your code as it stands will return an status code of STATUS_BUFFER_ALL_ZEROS so don't be tricked into thinking that this non-zero return value indicates failure.
One final point, the first parameter to both P/invokes, CompressionFormat, should be declared as ushort.

Related

Using DeviceIoControl from C# code always returns empty output buffer

I have a driver, which I want to use from my C# client app. The issue here is that my output buffer is always empty (0). When I use this driver from C code - everything works like a charm, so I think the issue is in my client C# code.
Extern is defined as below:
[DllImport(kernel, EntryPoint = "DeviceIoControl", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DeviceIoControl(
SafeFileHandle hDevice,
UInt32 dwIoControlCode,
IntPtr lpInBuffer,
UInt32 nInBufferSize,
IntPtr lpOutBuffer,
UInt32 nOutBufferSize,
ref UInt32 lpBytesReturned,
[In] ref NativeOverlapped lpOverlapped);
And I am using it as:
public static T ReadVirtualMemory<T>(SafeFileHandle driverHandle, int offset) where T : unmanaged
{
var inBuffer = (object)new T();
var nInBufferSize = Marshal.SizeOf(typeof(T));
var outBuffer = (object)new T();
var nOutBufferSize = Marshal.SizeOf(typeof(T));
var data = new KERNEL_READ_REQUEST
{
Address = (ulong)offset,
Size = (ulong)nInBufferSize,
pBuffer = (IntPtr)inBuffer
};
IntPtr lpInBuffer = IntPtr.Zero;
IntPtr lpOutBuffer = IntPtr.Zero;
nInBufferSize = Marshal.SizeOf(data);
lpInBuffer = Marshal.AllocHGlobal(nInBufferSize);
Marshal.StructureToPtr(data, lpInBuffer, true);
lpOutBuffer = Marshal.AllocHGlobal(nOutBufferSize);
Marshal.StructureToPtr(outBuffer, lpOutBuffer, true);
UInt32 lpBytesReturned = 0;
NativeOverlapped lpOverlapped = new NativeOverlapped();
Kernel32.DeviceIoControl(
driverHandle,
(uint)DriverMethod.ReadMemory,
lpInBuffer,
(uint)nInBufferSize,
lpOutBuffer,
(uint)nOutBufferSize,
ref lpBytesReturned,
ref lpOverlapped);
outBuffer = (T)Marshal.PtrToStructure(lpOutBuffer, typeof(T));
return lpBytesReturned == nOutBufferSize ? (T)outBuffer : default;
}
I am not sure why, bytes returned = 8, though it should be 4. And as I've said - out buffer is empty.
Drver's code:
PKERNEL_READ_REQUEST readRequest = (PKERNEL_READ_REQUEST)pIrp->AssociatedIrp.SystemBuffer;
PEPROCESS process;
if (NT_SUCCESS(PsLookupProcessByProcessId(ProcessId, &process)))
{
DebugMessage("ReadRequest requested\n");
KernelReadVirtualMemory(process, readRequest->Address, readRequest->pBuffer, readRequest->Size);
byteIo = sizeof(PKERNEL_READ_REQUEST);
status = STATUS_SUCCESS;
}
and
NTSTATUS NTAPI MmCopyVirtualMemory
(
PEPROCESS SourceProcess,
PVOID SourceAddress,
PEPROCESS TargetProcess,
PVOID TargetAddress,
SIZE_T BufferSize,
KPROCESSOR_MODE PreviousMode,
PSIZE_T ReturnSize
);
NTSTATUS KernelReadVirtualMemory(PEPROCESS process, PVOID sourceAddress, PVOID targetAddress, SIZE_T size)
{
PSIZE_T bytes;
return MmCopyVirtualMemory(process, sourceAddress, PsGetCurrentProcess(), targetAddress, size, KernelMode, &bytes);
}
Probably this is smth about structure alignment, but I am not sure (In C client app sizeof structure is 18 bytes, in C# 32 bytes).
Please advise
First - I had to compile in x64.
Second - had to allocate memory for pBuffer
Below is a working example
var nInBufferSize = Marshal.SizeOf(typeof(T));
var inBuffer = Marshal.AllocHGlobal(nInBufferSize);
var data = new KERNEL_READ_REQUEST
{
Address = offset,
Size = nInBufferSize,
pBuffer = inBuffer
};
var requestSize = Marshal.SizeOf(data);
var requestBuffer = Marshal.AllocHGlobal(requestSize);
Marshal.StructureToPtr(data, requestBuffer, true);
uint bytesReturned = 0;
var overlapped = new NativeOverlapped();
Kernel32.DeviceIoControl(
driverHandle,
(uint)DriverMethod.ReadMemory,
requestBuffer,
(uint)requestSize,
requestBuffer,
(uint)requestSize,
ref bytesReturned,
ref overlapped);
var result = Marshal.PtrToStructure(data.pBuffer, typeof(T));
return (T?)result ?? default;

Disable/Enable Windows 10 Devices Programmatically

I've been trying to disable/enable Windows 10 devices immidiately with WPF. There is an answer here but it give me an exception as mentioned in the answer's comments. There also an suggestion for fixing also in the answer's comments but I don't know how to make it work because I'm new to WPF.
Here is the code:
public static class DisableHardware
{
const uint DIF_PROPERTYCHANGE = 0x12;
const uint DICS_ENABLE = 1;
const uint DICS_DISABLE = 2; // disable device
const uint DICS_FLAG_GLOBAL = 1; // not profile-specific
const uint DIGCF_ALLCLASSES = 4;
const uint DIGCF_PRESENT = 2;
const uint ERROR_INVALID_DATA = 13;
const uint ERROR_NO_MORE_ITEMS = 259;
const uint ERROR_ELEMENT_NOT_FOUND = 1168;
static DEVPROPKEY DEVPKEY_Device_DeviceDesc;
static DEVPROPKEY DEVPKEY_Device_HardwareIds;
[StructLayout(LayoutKind.Sequential)]
struct SP_CLASSINSTALL_HEADER
{
public UInt32 cbSize;
public UInt32 InstallFunction;
}
[StructLayout(LayoutKind.Sequential)]
struct SP_PROPCHANGE_PARAMS
{
public SP_CLASSINSTALL_HEADER ClassInstallHeader;
public UInt32 StateChange;
public UInt32 Scope;
public UInt32 HwProfile;
}
[StructLayout(LayoutKind.Sequential)]
struct SP_DEVINFO_DATA
{
public UInt32 cbSize;
public Guid classGuid;
public UInt32 devInst;
public IntPtr reserved; // CHANGE #1 - was UInt32
}
[StructLayout(LayoutKind.Sequential)]
struct DEVPROPKEY
{
public Guid fmtid;
public UInt32 pid;
}
[DllImport("setupapi.dll", SetLastError = true)]
static extern IntPtr SetupDiGetClassDevsW(
[In] ref Guid ClassGuid,
[MarshalAs(UnmanagedType.LPWStr)]
string Enumerator,
IntPtr parent,
UInt32 flags);
[DllImport("setupapi.dll", SetLastError = true)]
static extern bool SetupDiDestroyDeviceInfoList(IntPtr handle);
[DllImport("setupapi.dll", SetLastError = true)]
static extern bool SetupDiEnumDeviceInfo(IntPtr deviceInfoSet,
UInt32 memberIndex,
[Out] out SP_DEVINFO_DATA deviceInfoData);
[DllImport("setupapi.dll", SetLastError = true)]
static extern bool SetupDiSetClassInstallParams(
IntPtr deviceInfoSet,
[In] ref SP_DEVINFO_DATA deviceInfoData,
[In] ref SP_PROPCHANGE_PARAMS classInstallParams,
UInt32 ClassInstallParamsSize);
[DllImport("setupapi.dll", SetLastError = true)]
static extern bool SetupDiChangeState(
IntPtr deviceInfoSet,
[In] ref SP_DEVINFO_DATA deviceInfoData);
[DllImport("setupapi.dll", SetLastError = true)]
static extern bool SetupDiGetDevicePropertyW(
IntPtr deviceInfoSet,
[In] ref SP_DEVINFO_DATA DeviceInfoData,
[In] ref DEVPROPKEY propertyKey,
[Out] out UInt32 propertyType,
IntPtr propertyBuffer,
UInt32 propertyBufferSize,
out UInt32 requiredSize,
UInt32 flags);
[DllImport("setupapi.dll", SetLastError = true)]
static extern bool SetupDiGetDeviceRegistryPropertyW(
IntPtr DeviceInfoSet,
[In] ref SP_DEVINFO_DATA DeviceInfoData,
UInt32 Property,
[Out] out UInt32 PropertyRegDataType,
IntPtr PropertyBuffer,
UInt32 PropertyBufferSize,
[In,Out] ref UInt32 RequiredSize
);
static DisableHardware()
{
DisableHardware.DEVPKEY_Device_DeviceDesc = new DEVPROPKEY();
DEVPKEY_Device_DeviceDesc.fmtid = new Guid(
0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67,
0xd1, 0x46, 0xa8, 0x50, 0xe0);
DEVPKEY_Device_DeviceDesc.pid = 2;
DEVPKEY_Device_HardwareIds = new DEVPROPKEY();
DEVPKEY_Device_HardwareIds.fmtid = new Guid(
0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67,
0xd1, 0x46, 0xa8, 0x50, 0xe0);
DEVPKEY_Device_HardwareIds.pid = 3;
}
public static void DisableDevice(Func<string, bool> filter, bool disable = true)
{
IntPtr info = IntPtr.Zero;
Guid NullGuid = Guid.Empty;
try
{
info = SetupDiGetClassDevsW(
ref NullGuid,
null,
IntPtr.Zero,
DIGCF_ALLCLASSES);
CheckError("SetupDiGetClassDevs");
SP_DEVINFO_DATA devdata = new SP_DEVINFO_DATA();
devdata.cbSize = (UInt32)Marshal.SizeOf(devdata);
// Get first device matching device criterion.
for (uint i = 0; ; i++)
{
SetupDiEnumDeviceInfo(info,
i,
out devdata);
// if no items match filter, throw
if (Marshal.GetLastWin32Error() == ERROR_NO_MORE_ITEMS)
CheckError("No device found matching filter.", 0xcffff);
CheckError("SetupDiEnumDeviceInfo");
string devicepath = GetStringPropertyForDevice(info,
devdata, 1); // SPDRP_HARDWAREID
// Uncomment to print name/path
//Console.WriteLine(GetStringPropertyForDevice(info,
// devdata, DEVPKEY_Device_DeviceDesc));
//Console.WriteLine(" {0}", devicepath);
if (devicepath != null && filter(devicepath)) break;
}
SP_CLASSINSTALL_HEADER header = new SP_CLASSINSTALL_HEADER();
header.cbSize = (UInt32)Marshal.SizeOf(header);
header.InstallFunction = DIF_PROPERTYCHANGE;
SP_PROPCHANGE_PARAMS propchangeparams = new SP_PROPCHANGE_PARAMS();
propchangeparams.ClassInstallHeader = header;
propchangeparams.StateChange = disable ? DICS_DISABLE : DICS_ENABLE;
propchangeparams.Scope = DICS_FLAG_GLOBAL;
propchangeparams.HwProfile = 0;
SetupDiSetClassInstallParams(info,
ref devdata,
ref propchangeparams,
(UInt32)Marshal.SizeOf(propchangeparams));
CheckError("SetupDiSetClassInstallParams");
SetupDiChangeState(
info,
ref devdata);
CheckError("SetupDiChangeState");
}
finally
{
if (info != IntPtr.Zero)
SetupDiDestroyDeviceInfoList(info);
}
}
private static void CheckError(string message, int lasterror = -1)
{
int code = lasterror == -1 ? Marshal.GetLastWin32Error() : lasterror;
if (code != 0)
throw new ApplicationException(
String.Format("Error disabling hardware device (Code {0}): {1}",
code, message));
}
private static string GetStringPropertyForDevice(IntPtr info, SP_DEVINFO_DATA devdata,
uint propId)
{
uint proptype, outsize;
IntPtr buffer = IntPtr.Zero;
try
{
uint buflen = 512;
buffer = Marshal.AllocHGlobal((int)buflen);
outsize=0;
// CHANGE #2 - Use this instead of SetupDiGetDeviceProperty
SetupDiGetDeviceRegistryPropertyW(
info,
ref devdata,
propId,
out proptype,
buffer,
buflen,
ref outsize);
byte[] lbuffer = new byte[outsize];
Marshal.Copy(buffer, lbuffer, 0, (int)outsize);
int errcode = Marshal.GetLastWin32Error();
if (errcode == ERROR_INVALID_DATA) return null;
CheckError("SetupDiGetDeviceProperty", errcode);
return Encoding.Unicode.GetString(lbuffer);
}
finally
{
if (buffer != IntPtr.Zero)
Marshal.FreeHGlobal(buffer);
}
}
}
The suggested fix is:
The problem is that, the function uses a fixed buffer length of 512, which a lot of times is not enough to hold the data. What needs to be done is to first get the size of buffer as follows: if(!SetupDiGetDeviceRegistryPropertyW(info, ref devdata, propId, out proptype, IntPtr.Zero, 0, ref outsize)). Once the size is got, allocate the buffer and call SetupDiGetDeviceRegistryPropertyW again
I haven't messed with this stuff before, but just reading the comment on that other question, it looks like the suggestion is to do this:
private static string GetStringPropertyForDevice(IntPtr info, SP_DEVINFO_DATA devdata, uint propId)
{
uint proptype, outsize;
IntPtr buffer = IntPtr.Zero;
try
{
SetupDiGetDeviceRegistryPropertyW(
info,
ref devdata,
propId,
out proptype,
IntPtr.Zero,
0,
ref outsize);
uint buflen = outsize;
buffer = Marshal.AllocHGlobal((int)buflen);
outsize = 0;
SetupDiGetDeviceRegistryPropertyW(
info,
ref devdata,
propId,
out proptype,
buffer,
buflen,
ref outsize);
byte[] lbuffer = new byte[outsize];
Marshal.Copy(buffer, lbuffer, 0, (int)outsize);
int errcode = Marshal.GetLastWin32Error();
if (errcode == ERROR_INVALID_DATA) return null;
CheckError("SetupDiGetDeviceProperty", errcode);
return Encoding.Unicode.GetString(lbuffer);
}
finally
{
if (buffer != IntPtr.Zero)
Marshal.FreeHGlobal(buffer);
}
}
Call SetupDiGetDeviceRegistryPropertyW with no buffer first, just to get the outsize value, then use that as your buffer size. This avoids creating a buffer of arbitrary length which could be exceeded, at the cost of making the call twice.
Again- I haven't tested this or anything, it just seems like what the person writing that comment was intending. Hopefully it works!

Unable to decrypt with CryptEncrypt/CryptDecrypt in C#

I've made a small application to encrypt and decrypt some text. All is fine as long as I use the byte array straight from the encryption. But as soon as I make a copy of the array to mimic the process of sending the encrypted text as a file the decryption will not run.
Why am I unable the run the decryption using a copied array?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Security.Cryptography;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Runtime.InteropServices;
using System.IO;
namespace EncryptDecryptApplication
{
class Program
{
static void Main(string[] args)
{
//Text to encrypt
string plaintext = "Text to encrypt and decrypt!";
// Password
const string password = "password";
// Constants used in cryptography functions
const string MS_ENH_RSA_AES_PROV = "Microsoft Enhanced RSA and AES Cryptographic Provider"; //Name of provider. Same as "Microsoft AES Cryptographic Provider".
const byte PROV_RSA_AES = 24; //Type of provider
const string KeyContainer = null; //Name of the key container to be used, if NULL then a default key container name is used. Must be a null-terminated string.
const uint ALG_CLASS_HASH = (4 << 13); //32768 = 4*2^13; //Samma tror jag för alla hashalgoritmer
const uint ALG_TYPE_ANY = (0); //Samma tror jag för alla hashalgoritmer
const uint ALG_SID_SHA_256 = 12; //ALG_SID_MD5 = 3, ALG_SID_SHA = 4, ALG_SID_SHA_256 = 12, ALG_SID_SHA_384 = 13, ALG_SID_SHA_512 = 14
const uint CALG_SHA_256 = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_256);
const int ALG_CLASS_DATA_ENCRYPT = 24576;
const int ALG_TYPE_BLOCK = 1536; //used in all types of AES, and in RC2
const int ALG_SID_AES_256 = 16; //ALG_SID_AES_128 = 14, ALG_SID_AES_192 = 15, ALG_SID_AES_256 = 16
const int CALG_AES_256 = (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_AES_256);
const int ENCRYPT_ALGORITHM = CALG_AES_256;
// Obtain handle to Cryptographic Service Provider (CSP)
string ProviderCSP = MS_ENH_RSA_AES_PROV + null; //name of the CSP to be used. Must be a null-terminated string.
IntPtr CSPhandle = new IntPtr();
Crypt32.CryptAcquireContext(ref CSPhandle, KeyContainer, ProviderCSP, PROV_RSA_AES, 0);
//Create hash object
IntPtr handleHashObj = new IntPtr();
Crypt32.CryptCreateHash(CSPhandle, CALG_SHA_256, IntPtr.Zero, 0, ref handleHashObj);
//Hash password
byte[] pwByteArray = Encoding.Unicode.GetBytes(password);
uint pwByteAmt = (uint)ASCIIEncoding.Unicode.GetByteCount(password);
Crypt32.CryptHashData(handleHashObj, pwByteArray, pwByteAmt, 0);
//Dervie session key from the hashed password
IntPtr handleSessionKey = new IntPtr();
Crypt32.CryptDeriveKey(CSPhandle, ENCRYPT_ALGORITHM, handleHashObj, 0, ref handleSessionKey);
//CryptEncrypt iteration no 1 - Obtain buffer size (output ByteAmt_Itr1)
byte[] byteArray = new byte[plaintext.Length * sizeof(char)];
System.Buffer.BlockCopy(plaintext.ToCharArray(), 0, byteArray, 0, byteArray.Length);
uint byteAmt_Itr1 = (uint)byteArray.Length; //No of bytes, i.e. the size, of the plaintext.
uint bufferSize_Itr1 = byteAmt_Itr1; //Set buffer size to input data size for now
Crypt32.CryptEncrypt(handleSessionKey, IntPtr.Zero, 1, 0, null, ref byteAmt_Itr1, 0);
//CryptEncrypt iteration no 2 - Encryption
uint byteAmt_Itr2 = (uint)byteArray.Length; //No of bytes, i.e. the size, of the plaintext.
uint bufferSize_Itr2 = byteAmt_Itr1; //Output from iteration no 1 - size of output data, i.e. correct buffer size
Crypt32.CryptEncrypt(handleSessionKey, IntPtr.Zero, 1, 0, byteArray, ref byteAmt_Itr2, bufferSize_Itr2);
Console.WriteLine("Encrypted: " + Encoding.Default.GetString(byteArray));
// Text encrypted as byteArray, try to decrypt it! //
//CryptDecrypt - with input from CryptEncrypt".
Console.WriteLine(Crypt32.CryptDecrypt(handleSessionKey, IntPtr.Zero, 1, 0, byteArray, ref byteAmt_Itr2));
//Convert decrypted byte array to string
char[] chars = new char[byteArray.Length / sizeof(char)];
System.Buffer.BlockCopy(byteArray, 0, chars, 0, byteArray.Length);
string decryptedText = new string(chars);
Console.WriteLine("Decrypted: " + decryptedText);
}
}
public class Crypt32
{
[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptAcquireContext(
ref IntPtr hProv,
string pszContainer,
string pszProvider,
uint dwProvType,
uint dwFlags);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptCreateHash(
IntPtr hProv,
uint algId,
IntPtr hKey,
uint dwFlags,
ref IntPtr phHash);
[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptHashData(
IntPtr hHash,
byte[] pbData,
uint dataLen,
uint flags);
[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptDeriveKey(
IntPtr hProv,
int Algid,
IntPtr hBaseData,
int flags,
ref IntPtr phKey);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptEncrypt(
IntPtr hKey,
IntPtr hHash,
int Final,
uint dwFlags,
byte[] pbData,
ref uint pdwDataLen,
uint dwBufLen);
[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptDecrypt(
IntPtr hKey,
IntPtr hHash,
int Final,
uint dwFlags,
byte[] pbData,
ref uint pdwDataLen);
}
}
Output:
Encrypted:
B'♦tt'sô?*ý¢┼àò⌂9Z▼?£'$'¥«çæOÆà[/ë?·UÛÙªÄ2?┼[É{&IâínaÇe
True
Decrypted: Text to encrypt and decrypt!
I've sorted out the other inputs to CryptDecrypt (have made another handleSessionKey and also another variable instead of byteAtm_Itr2), they are not the source of the problem. The problem seems to be with the byte array.
If I use this code (or Buffer.BlockCopy) to copy the array the decryption will fail:
byte[] byteArray2 = new byte[byteArray.Length];
byteArray.CopyTo(byteArray2, 0);
The arrays are the same but of course not the same object. Can't work out why this wouldn't work. If I run the code with the copied array this is the output:
Encrypted:
B'♦tt'sô?*ý¢┼àò⌂9Z▼?£'$'¥«çæOÆà[/ë?·UÛÙªÄ2?┼[É{&IâínaÇe
False
Decrypted: Text to encrypt and decr????
Because you are using Unicode, that uses two bytes per character, you are not reserving enough space for the original byteArray. You need to allocate twice as much space as your currently doing:
byte[] byteArray = new byte[2*plaintext.Length*sizeof (char)];
After changing this code, you can safely copy the array and decrypt the copied array. It seems can decrypt the original array as the Marshalled library correctly recognised the array length (that it created in the first place). However, when using Array.Copy these additional bytes do not get transferred by the .NET code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Security.Cryptography;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Runtime.InteropServices;
using System.IO;
namespace EncryptDecryptApplication
{
internal class Program
{
private static void Main(string[] args)
{
//Text to encrypt
string plaintext = "Text to encrypt and decrypt!";
// Password
const string password = "password";
// Constants used in cryptography functions
const string MS_ENH_RSA_AES_PROV = "Microsoft Enhanced RSA and AES Cryptographic Provider";
//Name of provider. Same as "Microsoft AES Cryptographic Provider".
const byte PROV_RSA_AES = 24; //Type of provider
const string KeyContainer = null;
//Name of the key container to be used, if NULL then a default key container name is used. Must be a null-terminated string.
const uint ALG_CLASS_HASH = (4 << 13); //32768 = 4*2^13; //Samma tror jag för alla hashalgoritmer
const uint ALG_TYPE_ANY = (0); //Samma tror jag för alla hashalgoritmer
const uint ALG_SID_SHA_256 = 12;
//ALG_SID_MD5 = 3, ALG_SID_SHA = 4, ALG_SID_SHA_256 = 12, ALG_SID_SHA_384 = 13, ALG_SID_SHA_512 = 14
const uint CALG_SHA_256 = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_256);
const int ALG_CLASS_DATA_ENCRYPT = 24576;
const int ALG_TYPE_BLOCK = 1536; //used in all types of AES, and in RC2
const int ALG_SID_AES_256 = 16; //ALG_SID_AES_128 = 14, ALG_SID_AES_192 = 15, ALG_SID_AES_256 = 16
const int CALG_AES_256 = (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_AES_256);
const int ENCRYPT_ALGORITHM = CALG_AES_256;
// Obtain handle to Cryptographic Service Provider (CSP)
string ProviderCSP = MS_ENH_RSA_AES_PROV + null;
//name of the CSP to be used. Must be a null-terminated string.
IntPtr CSPhandle = new IntPtr();
Crypt32.CryptAcquireContext(ref CSPhandle, KeyContainer, ProviderCSP, PROV_RSA_AES, 0);
//Create hash object
IntPtr handleHashObj = new IntPtr();
Crypt32.CryptCreateHash(CSPhandle, CALG_SHA_256, IntPtr.Zero, 0, ref handleHashObj);
//Hash password
byte[] pwByteArray = Encoding.Unicode.GetBytes(password);
uint pwByteAmt = (uint) ASCIIEncoding.Unicode.GetByteCount(password);
Crypt32.CryptHashData(handleHashObj, pwByteArray, pwByteAmt, 0);
//Dervie session key from the hashed password
IntPtr handleSessionKey = new IntPtr();
Crypt32.CryptDeriveKey(CSPhandle, ENCRYPT_ALGORITHM, handleHashObj, 0, ref handleSessionKey);
//CryptEncrypt iteration no 1 - Obtain buffer size (output ByteAmt_Itr1)
byte[] byteArray = new byte[2*plaintext.Length*sizeof (char)];
System.Buffer.BlockCopy(plaintext.ToCharArray(), 0, byteArray, 0, byteArray.Length/2);
uint byteAmt_Itr1 = (uint) byteArray.Length; //No of bytes, i.e. the size, of the plaintext.
uint bufferSize_Itr1 = byteAmt_Itr1; //Set buffer size to input data size for now
Crypt32.CryptEncrypt(handleSessionKey, IntPtr.Zero, 1, 0, null, ref byteAmt_Itr1, 0);
//CryptEncrypt iteration no 2 - Encryption
uint byteAmt_Itr2 = (uint) byteArray.Length; //No of bytes, i.e. the size, of the plaintext.
uint bufferSize_Itr2 = byteAmt_Itr1;
//Output from iteration no 1 - size of output data, i.e. correct buffer size
Crypt32.CryptEncrypt(handleSessionKey, IntPtr.Zero, 1, 0, byteArray, ref byteAmt_Itr2, bufferSize_Itr2);
Console.WriteLine("Encrypted: " + Encoding.Default.GetString(byteArray));
// Text encrypted as byteArray, try to decrypt it! //
byte[] byteArray2 = new byte[byteArray.Length];
byteArray.CopyTo(byteArray2, 0);
//CryptDecrypt - with input from CryptEncrypt".
Console.WriteLine(Crypt32.CryptDecrypt(handleSessionKey, IntPtr.Zero, 1, 0, byteArray2, ref byteAmt_Itr2));
//Convert decrypted byte array to string
string decryptedText = Encoding.Unicode.GetString(byteArray2).Split(new char[] { '\0' }, StringSplitOptions.RemoveEmptyEntries)[0];
//char[] chars = new char[byteArray.Length / sizeof(char)];
//System.Buffer.BlockCopy(byteArray, 0, chars, 0, byteArray.Length);
//string decryptedText = new string(chars);
Console.WriteLine("Decrypted: " + decryptedText);
}
}
public class Crypt32
{
[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptAcquireContext(
ref IntPtr hProv,
string pszContainer,
string pszProvider,
uint dwProvType,
uint dwFlags);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptCreateHash(
IntPtr hProv,
uint algId,
IntPtr hKey,
uint dwFlags,
ref IntPtr phHash);
[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptHashData(
IntPtr hHash,
byte[] pbData,
uint dataLen,
uint flags);
[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptDeriveKey(
IntPtr hProv,
int Algid,
IntPtr hBaseData,
int flags,
ref IntPtr phKey);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptEncrypt(
IntPtr hKey,
IntPtr hHash,
int Final,
uint dwFlags,
byte[] pbData,
ref uint pdwDataLen,
uint dwBufLen);
[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptDecrypt(
IntPtr hKey,
IntPtr hHash,
int Final,
uint dwFlags,
byte[] pbData,
ref uint pdwDataLen);
}
}

GetQueuedCompletionStatus blocking file to open c#

I am trying to convert a driver client from c to c#. I am able to make it work completely in c but as I am converting it in c# it is blocking the file to open the code for your reference is as follows:
[DllImport("fltLib.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Auto, SetLastError = true)]
public static extern unsafe int FilterGetMessage(
IntPtr hPort,
IntPtr lpMessageBuffer,
int dwMessageBufferSize,
void* Overlapped
);
IntPtr buf = Marshal.AllocHGlobal(
Marshal.SizeOf(msg.MessageHeader));
OVERLAPPED povlp;
Marshal.StructureToPtr(msg.MessageHeader, buf, false);
var sizeUserRec = Marshal.SizeOf(typeof(OVERLAPPED));
var userRec = Marshal.AllocHGlobal(sizeUserRec);
povlp.Internal = UIntPtr.Zero;
povlp.InternalHigh = UIntPtr.Zero;
povlp.Offset = 0;
povlp.OffsetHigh = 0;
povlp.Pointer = IntPtr.Zero;
povlp.hEvent = IntPtr.Zero;
headerSize = Marshal.SizeOf(msg.MessageHeader);
msgsize += headerSize;
unsafe
{
status = FilterGetMessage(
portPtr,
buf,
msgsize,
&povlp
);
}
[DllImport("kernel32.dll")]
static extern bool GetQueuedCompletionStatus(IntPtr CompletionPort, ref uint
lpNumberOfBytes, ref IntPtr lpCompletionKey, ref IntPtr lpOverlapped,
uint dwMilliseconds);
static _OVERLAPPED Ovlp;
Console.WriteLine("int sddc");
_SCANNER_NOTIFICATION notification;
_SCANNER_REPLY_MESSAGE replyMessage;
IntPtr pOvlp;
pOvlp = Marshal.AllocHGlobal(Marshal.SizeOf(Ovlp));
Marshal.StructureToPtr(Ovlp, pOvlp, false);
_SCANNER_MESSAGE message;
bool result;
uint outSize = 3435973836, milisec = 0xFFFFFFFF;
int hr;
IntPtr key = IntPtr.Zero;
result = GetQueuedCompletionStatus(Context.Completion, ref outSize, ref key, ref pOvlp,uint.MaxValue); //error line
if (!result)
{
Console.WriteLine("Not valid");
}

Read a specific sector of DVD by Pinvoke in C#

I am reading directly from a disk using C# and pinvoking the kernel32 ReadFile method.i want just read a particular sector for save time but ReadFile read from first to N sector. How can read only own sector with my choice?
[StructLayout(LayoutKind.Sequential)]
public struct OVERLAPPED
{
public uint Internal;
public uint InternalHigh;
public uint Offset;
public uint OffsetHigh;
public int hEvent;
}
[DllImport("kernel32", SetLastError = true)]
static extern int CreateFile(string filename, uint desiredAccess, uint shareMode, IntPtr attributes, uint creationDisposition, uint flagsAndAttributes, IntPtr templateFile);
[DllImport("kernel32", SetLastError = true)]
public static extern Boolean CloseHandle(int handle);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern Boolean ReadFile(IntPtr hFile, Byte[] buffer, UInt32 BytesToRead, ref UInt32 BytedRead, OVERLAPPED OverLapped);
static int EIGHT_K = 8192;
static int FIVE_TWELVE_BYTES = 512;
static uint GENERIC_READ = 0x80000000;
static uint OPEN_EXISTING = 3;
static uint FILE_SHARE_READ = 1;
static uint FILE_SHARE_WRITE = 2;
[STAThread]
private void button1_Click(object sender, EventArgs e)
{
int fileHandle = 0;
bool returnVal = true;
try
{
// Open the device specified (Using the boot partition)
string deviceName = #"\\.\f:";
fileHandle = CreateFile(deviceName, GENERIC_READ,FILE_SHARE_READ | FILE_SHARE_WRITE, (IntPtr)0, OPEN_EXISTING, 0,(IntPtr)0);
if (fileHandle != -1)
{
Byte[] sector = new Byte[EIGHT_K];
UInt32 bytesRead = (uint)EIGHT_K;
OVERLAPPED ol = new OVERLAPPED();
// Can't get a FileStream ctor to work so I am using Win32 API ReadFile
bool worked = ReadFile((IntPtr)fileHandle, sector, (uint)EIGHT_K, ref bytesRead, ol);
return;
}
}
catch (Exception ex)
{
return;
}
finally
{
CloseHandle(fileHandle);
}
return;
}
I want to mark the DVD till required Original DVD to run the program.
Your OVERLAPPED struct is declared poorly and is incorrect in a 64 bit process. But in any case you don't need it. You are not performing overlapped I/O. Which is just as well because the declaration of ReadFile is incorrect. That function wants a pointer to an OVERLAPPED struct. You pass it by value.
In any case, you just don't need to consider overlapped I/O. So fix this issue by deleting the OVERLAPPED struct declaration from your code. And declare ReadFile like this:
[DllImport("kernel32.dll", SetLastError = true)]
public static extern Boolean ReadFile(IntPtr hFile, Byte[] buffer,
UInt32 BytesToRead, out UInt32 BytedRead, IntPtr Overlapped);
Pass IntPtr.Zero as the Overlapped parameter. And do make sure that you check the return value of ReadFile for an error.
The next step is to seek to a location in the file. Use SetFilePointerEx for that.
DllImport("kernel32.dll")]
static extern bool SetFilePointerEx(IntPtr hFile, long liDistanceToMove,
out long lpNewFilePointer, uint dwMoveMethod);
Consult the documentation for SetFilePointerEx to work out how to call this function.
Since you are using direct disk access, you will of course need to align the reads to sector boundaries.

Categories