Вот интересная проблема:
Я использую ReadProcessMemory (из C #) для написания простой программы отладчика. Мне нужно пройти через все пространство памяти целевого процесса, чтобы найти определенные строки байтов (FWIW, я использую Бойера-Мура, чтобы сэкономить время, это круто).
Для этого я использую ReadProcessMemory, чтобы скопировать большие блоки памяти, выполнить итерацию по ней в моей программе, а затем перейти к следующему блоку (да, я также принимаю во внимание случай, когда значение может находиться между граница между двумя блоками).
Однако ReadProcessMemory возвращает разные значения в зависимости от размера буфера, в который он должен копироваться. Для моего исследования я использовал ReadProcessMemory на calc.exe (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 (я тестировал 64, 512, 1024, 2048, 4096, 8192, 16384, 32768 и 65536), тогда вызовы ReadProcessMemory завершаются ошибкой, пока значение blockAddr
равен 0x10000 (65536), и в этот момент ReadProcessMemory возвращает TRUE и сообщает ненулевые значения bytesRead.
Однако, когда BlockSize
равен 20480 (20 * 2048, то есть 20 КБ, что не является степенью двойки), функция возвращает TRUE, только если blockAddr
равно 0x14000 (81920), что странно, поскольку 32768 и 65536 размеров блоков больше, чем 20480, но возвращаются, когда blockAddr равен 0x10000.
Когда я использую блоки еще больших размеров (включая 128 КБ и 1024 КБ), значение blockAddr становится еще выше, для 128 КБ это было 0x60000, а для 1 МБ это было 0x600000.
Ясно, что я должен ограничить свою программу блоками памяти размером 64 КБ, чтобы не иметь возможности прочитать всю память процесса, что означает, что моя программа больше не будет корректной, но зачем нужен простой размер буфера влияет на корректность программы? Все, что делает Windows - это простая копия памяти.
FWIW, я использую Windows 7 x64. Моя программа на C # скомпилирована с AnyCPU, поэтому она работает как x64. Целевая программа - C: \ Windows \ calc.exe, также x64.