首页 > 解决方案 > 用偏移量读取内存值的通用方法

问题描述

[DllImport("kernel32.dll", EntryPoint = "ReadProcessMemory")]
public static extern int ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, 
                                          [In, Out] byte[] buffer, int size, out IntPtr lpNumberOfBytesRead);

private byte[] ReadBytes(Process process, MemoryAddress address, int bytesToRead)
{
    var value = new byte[bytesToRead];
    var baseAddress = GetBaseAddressByModuleName(process, address.Module.Value) + address.BaseAddress;
    var handle = GetProcessHandle(process);

    ReadProcessMemory(handle, (IntPtr)baseAddress, value, bytesToRead, out var bytesRead);

    foreach (var offset in address.Offsets)
    {
        ReadProcessMemory(handle,
            new IntPtr(BitConverter.ToInt32(value, 0) + offset),
            value,
            bytesToRead,
            out bytesRead);
    }

    return value;
}

private static int GetSize<T>()
{
    return Marshal.SizeOf(typeof(T));
}

这与 float、int、double 和 strings 一起工作正常:

public long ReadLong(Process process, MemoryAddress address)
{
    return ReadBytes(process, address, GetSize<long>()).ConvertTo<long>();
}

这是我的问题(ConvertTo只是使用的扩展BitConverter。):

public short ReadShort(Process process, MemoryAddress address)
{
    return ReadBytes(process, address, GetSize<short>()).ConvertTo<short>();
}

public byte ReadByte(Process process, MemoryAddress address)
{
    return ReadBytes(process, address, 1)[0];
}

使用这些方法时,会在 foreach 循环中引发异常ReadBytesSystem.ArgumentException: 'Destination array is not long enough to copy all the items in the collection. Check array index and length.'

我想它与BitConverter.ToInt32. 使用ToInt16forshort可以消除异常,但会产生错误的结果。如何正确对待偏移量shortbyte值?

标签: c#pinvoke

解决方案


我明白你现在在做什么——你正在跟踪一系列对象引用,这些引用指向存储在最后一个对象中的值。像这样的东西应该适用于 32 位地址指针,这听起来像你正在做的事情:

private byte[] ReadBytes(Process process, MemoryAddress address, int bytesToRead)
{
    var value = new byte[bytesToRead];
    var currentAddress = new byte[4];
    var baseAddress = GetBaseAddressByModuleName(process, address.Module.Value) + address.BaseAddress;
    var handle = GetProcessHandle(process);

    ReadProcessMemory(handle, (IntPtr)baseAddress, currentAddress, 4, out var bytesRead);

    foreach (var offset in address.Offsets.Take(address.Offsets.Length - 1))
    {
        ReadProcessMemory(handle,
            new IntPtr(BitConverter.ToInt32(currentAddress, 0) + offset),
            currentAddress,
            4,
            out bytesRead);
    }

    ReadProcessMemory(handle,
        new IntPtr(BitConverter.ToInt32(currentAddress, 0) + address.Offsets.Last()),
        value,
        bytesToRead,
        out bytesRead);

    return value;
}


推荐阅读