RtlCompressBuffer API in C#

本文关键字:in API RtlCompressBuffer | 更新日期: 2023-09-27 18:02:14

我正在尝试在c#项目中使用RtlGetCompressionWorkSpaceSize和RtlCompressBuffer函数。

到目前为止我写的是:

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工作,因为它返回0 (NT成功代码),但当我调用RtlCompressBuffer时,我得到内存访问冲突错误。

编辑:在David的回答的帮助下,我已经修复了这个问题,正确的代码如下。
    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;
    }

RtlCompressBuffer API in C#

你就快成功了。问题是RtlCompressBuffer的P/调用的这一部分:

out byte[] DestinationBuffer

byte[]的默认封送是对数组内容在两个方向上进行封送,从托管到非托管,然后在函数返回时再返回。RtlCompressBuffer的C定义用__out注释,但这意味着数组内容是__out而不是指针是__out

将p/invoke更改为

byte[] DestinationBuffer

,同样在RtlCompressBuffer的调用中,将out outBuf更改为outBuf,您应该可以很好地运行。

请注意,您的代码将返回状态码STATUS_BUFFER_ALL_ZEROS,因此不要误以为此非零返回值表示失败。

最后一点,两个p/调用的第一个参数CompressionFormat应该声明为ushort