ReadProcessMemory - 缓冲区大小影响函数的正确性

时间:2011-04-11 02:17:41

标签: c# winapi

这是一个有趣的问题:

我正在使用ReadProcessMemory(在C#中)编写一个简单的调试程序。我需要通过一个目标进程'整个内存空间来查找某些字节串(FWIW,我使用Boyer-Moore来节省时间,这很酷)。

要做到这一点,我正在使用ReadProcessMemory复制大块内存,在我的程序中迭代它,然后转到下一个块(是的,我还考虑了值可能跨越的情况)两个街区之间的边界。)

但是,ReadProcessMemory返回不同的值,具体取决于它被告知要复制到的缓冲区的大小。对于我的调查,我在calc.exe(Windows 7 x64)上使用了ReadProcessMemory。我得到了一致的结果:

这是我的NativeMethods P / Invoke签名:

[DllImport("Kernel32.dll", CallingConvention=CallingConvention.Winapi, SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern Boolean ReadProcessMemory(IntPtr process, void* baseAddress, void* destBuffer, IntPtr size, out IntPtr bytesRead);

以下是我使用它的代码:

public IntPtr[] Search(Byte[] needle) {

    OpenProcess();

    List<IntPtr> ret = new List<IntPtr>();

    Int32 iterations = (int)( (MaxAddr32bit + 1) / BlockSize );

    IntPtr sizeOfBlock = new IntPtr( BlockSize );
    IntPtr bytesRead;

    byte* buffer = (byte*)Marshal.AllocHGlobal( sizeOfBlock );

    for(int i=0;i<iterations;i++) {

        void* blockAddr = (void*)(i * BlockSize);

        bool ok = NativeMethods.ReadProcessMemory( _process, blockAddr, buffer, sizeOfBlock, out bytesRead);

        if( bytesRead.ToInt64() > 0 ) {

            switch(needle.Length) {
                case  1: Search8 ( buffer, sizeOfBlock, ret, needle[0] ); break;
                case  2: Search16( buffer, sizeOfBlock, ret, needle[0], needle[1] ); break;
                case  4: Search32( buffer, sizeOfBlock, ret, needle[0], needle[1], needle[2], needle[3] ); break;
                case  8: Search64( buffer, sizeOfBlock, ret, needle[0], needle[1], needle[2], needle[3],  needle[4], needle[5], needle[6], needle[7] ); break;
            }
        }
    }

    Marshal.FreeHGlobal( new IntPtr(buffer) );

    CloseProcess();

    return ret.ToArray();
}

BlockSize是一个常数,我一直在变化并得到不同的结果。

BlockSize是2的幂小于或等于65536(我测试了64,512,1024,2048,4096,8192,16384,32768和65536)时,对ReadProcessMemory的调用将失败,直到值为止blockAddr的值为0x10000(65536),此时ReadProcessMemory返回TRUE并报告非零bytesRead值。

但是当BlockSize为20480(20 * 2048,又名20KB,这不是2的幂)时,该函数仅在blockAddr为0x14000(81920)时返回TRUE,这很奇怪,因为32768和65536块大小大于20480但是当blockAddr为0x10000时返回。

当我使用更大的块大小(包括128KB和1024KB)时,blockAddr值甚至更高,128KB时为0x60000,1MB则为0x600000。

显然,我必须将我的程序限制为64KB大小的内存块,以免无法读取所有进程的内存,这意味着我的程序将不再正确,但为什么要使用简单的缓冲区大小影响程序的正确性?所有Windows正在做的是一个简单的内存副本。

FWIW,我正在运行Windows 7 x64。我的程序是用AnyCPU编译的C#,所以它运行为x64。我所针对的程序是C:\ Windows \ calc.exe,它也是x64。

1 个答案:

答案 0 :(得分:2)

别猜这个。使用VirtualQueryEx()找出进程中内存的映射位置以及块的大小。