Strange behavior causing null reference when calling ReadProcessMemory

I am experiencing unusual behavior when called ReadProcessMemory

in C # via this P / Invoke signature:

[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ReadProcessMemory(
    IntPtr hProcess,
    IntPtr lpBaseAddress,
    [Out] byte[] lpBuffer,
    int dwSize,
    out int lpNumberOfBytesRead
    );

      


In my application, I go through all the memory of the memory areas that have read and write access (and some other filters apply, albeit a different part).

The scan code looks something like this:

int numberOfBytes;
if (!NativeMethods.ReadProcessMemory(handle, region.StartAddress,
    buffer, (int)region.RegionSize, out numberOfBytes))
// The handle, region (custom struct containing some fields from the
// MEMORY_BASIC_INFORMATION struct), and buffer come from parameters.

      

And the code works fine. It scans all memory for a sequence of bytes. No problems.


A little more in my program thread I have this code:
Note: it uses the same IntPtr descriptor as the previous code (tested it) and it works in one thread

int bytesRead;
byte[] buffer = new byte[128]; // In my real app this is some calculated value
                            // however that irrelevant. It calculated 128.
if (!NativeMethods.ReadProcessMemory(handle, location.Location,
    buffer, buffer.Length, out bytesRead))
    continue; // Error while reading
// At this point buffer == null, so the next line causes an exception
if (bytesRead != buffer.Length) continue;

      

The code is very similar, but for some reason the buffer reference is lost and the buffer is set to zero. If it wasn't for an external call, I would be 100% sure this is a bug, because the buffer is not passed as a parameter ref

or out

. However, I know that .NET does some vodoo stuff when it comes to external calls (for example, for marshaling).

What makes the situation even weirder is that when I replace this code:

int bytesRead;
byte[] buffer = new byte[128];
byte[] bufferRef = buffer;
if (!NativeMethods.ReadProcessMemory(handle, location.Location,
    buffer, buffer.Length, out bytesRead))
    continue; // Error while reading
buffer = bufferRef;
if (bytesRead != buffer.Length) continue;

      

The code just works. The memory is readable and that's it! So all that happens is that for some reason the variable buffer

loses its reference to the actual buffer. And it confuses me.


Is this the result of something I did wrong (for example a faulty P / Invoke), is it dangerous (memory leak?) And understandable?


My config:

  • .NET Framework 4.0
  • Visual Studio Professional 2012 (version 11.0.51106.01 Update 1)
  • .NET Framework 4.5.50709 installed
  • Works as an administrator
  • Happens in both release build and debug, both in visual studio host executable and standard build executable.
  • 64-bit versions of Windows 7
  • Process I am reading memory from 32-bit
  • Build configuration: platform: any processor

Edit: The complete NativeMethods class I'm using can be found here: http://paste2.org/p/2770271 p>

Edit2: I have added the simple steps I followed to fix the problem as the answer can be found here .

+3


source to share


4 answers


Probably since you are a 64-bit application, your lpNumberOfBytesRead must be "long", so the ReadProcessMemory call overwrites (part of your) pointer buffer on return.



+2


source


OutAttribute

of byte[] lpBuffer

, declared in your ReadProcessMemory request (which is the correct porting _Out_ LPVOID lpBuffer

), indicates that the data should be marshaled from the callee back to the caller ... so your byte array is probably referenced to zero by the callee itself (Kernel32).



0


source


Due to advice from Hans Passant and 500 - Internal Server Error (whom I labeled as the accepted answer) I was able to resolve the issue.

These are the steps I took:

  • I decided to use 32bit instead of Any CPU. (Mainly for backward compatibility.)
  • Then I updated the P / Invoke signatures using the MSDN pages for functions and this page on Windows data types. And chose for 32-bit signature options.
  • I ran the code again and the buffer reference was not cleared.

Thanks for the help.

0


source


Do not mark byte[] buffer

as parameter [Out]

. This is more similar ref

than out

what is already implied, since it is an array of bytes. Since integers ( int

here) are type values, the parameter numberOfBytesRead

requires a parameter out

. This is the only thing that needs to be marked with out

.

When something is marked as a parameter out

, the class Marshal

expects the callee ( ReadProcessMemory

here) to return a value. A byte array is simply a pointer (address) to a location in memory that contains bytes. You don't want this pointer to be written by the callee user.

-2


source







All Articles