2011-04-11 110 views
2

这里有一个有趣的问题:ReadProcessMemory - 缓冲区大小影响功能的正确性

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

为此,我使用ReadProcessMemory来复制大块内存,在我的程序中遍历它,然后继续到下一个块(是的,我还考虑了值可能跨越的情况两块之间的边界)。

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

这里是我的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功率(I测试的64,512,1024,2048,4096,8192,16384,32768,65536),然后调用ReadProcessMemory失败,直到blockAddr值是0x10000(65536),此时ReadProcessMemory返回TRUE并报告非零bytesRead值。

然而,当BlockSize是20480(20 * 2048,又名20KB,这不是二的幂),则该函数仅当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

不要猜测在这。使用VirtualQueryEx()来找出内存在进程中的映射位置以及块的大小。