通过 P/Invoke 将空终止字符串作为字节数组发送到 WriteProcessMemory()

Send null terminated string as byte array to WriteProcessMemory() via P/Invoke

我的 P/Invoke WriteProcessMemory 方法目前看起来像这样:

        [DllImport("kernel32", SetLastError = true)]
        [return : MarshalAs(UnmanagedType.Bool)]
        static extern bool WriteProcessMemory(
            IntPtr hProcess,
            IntPtr baseAddress,
            byte[] bufferToWrite,
            uint numBytesToWrite,
            out IntPtr numBytesWritten
            );

我需要将一个字符串发送到 bufferToWrite 参数,但它需要一个 byte[],所以我在传递它之前将其转换为 byte[]

我的问题是,由于 .NET 字符串不是以 null 结尾的,因此它不会将终止 null 写入远程内存。如果我在非托管代码中执行此操作,我将分配字符串的长度 + 终止空值,并将所有内容放入分配的内存中,因为我正在读取的 "string" 包含空值。但是托管字符串不包含它。

也许我可以分配一个足够大的缓冲区来包含终止空值,然后再次调用 WriteProcessMemory 来写入空值;或者我可以创建一个足够大的 byte[] 来容纳字符串 + null,将两者都写入数组,然后像那样传递它。

但是这个问题有更优雅的解决方案吗?

您还可以分配字符串加上空终止符。例如。如果你想写字符串

string szToWrite = "This is what I want to write";

我会将其转换为 byte[],例如使用 ASCII 转换

byte[] aBytes = Encoding.ASCII.GetBytes(szToWrite);

然后将它与一个额外的空字节连接起来。

aBytes = aBytes.Concat(new byte[] { 0x00 }).ToArray();

如果需要,创建您自己的函数 WriteProcessMemoryString,让它接受 string 而不是 byte[] 并使用与上述相同的概念。另请注意,您可能希望使用与 ASCII 不同的编码,例如UTF7/UTF8,或任何您本机需要的东西(例如宽字符字符串)。

像这样声明重载:

[DllImport("kernel32", SetLastError = true)]
[return : MarshalAs(UnmanagedType.Bool)]
static extern bool WriteProcessMemory(
    IntPtr hProcess,
    IntPtr baseAddress,
    string bufferToWrite,
    uint numBytesToWrite,
    out uint numBytesWritten
);

这样你就可以强制编组器以 null 终止字符串。

您可以将数据作为 IntPtr 传递并使用 Marshal.StringToPtrAnsi。但是你需要管理内存。

如果文本是 Unicode,请使用 CharSet.Unicode 选项。