John
John

Reputation: 7910

vb 2008 WriteProcessMemory() returns 0

hello i'm using vb 2008 here is part of my code:

Private Declare Function WriteProcessMemory Lib "kernel32" Alias "WriteProcessMemory" ( _
      ByVal hProcess As Integer, _
      ByVal lpBaseAddress As Integer, _
      ByVal lpBuffer As Integer, _
      ByVal nSize As Integer, _
      ByVal lpNumberOfBytesWritten As Integer _
   ) As Integer

    Dim proc() As Process = Process.GetProcessesByName("process")
    If proc.Length = 0 Then Console.WriteLine("Process Not Found") : Exit Sub
    Dim p = proc(0)
    Dim pH As Integer = OpenProcess(PROCESS_ALL_ACCESS, 0, p.Id)
    Dim address As Integer
    address = &H98544
    Dim memory As IntPtr
    Call ReadProcessMemory(pH, address, memory, 4, 0)
    Console.WriteLine(memory.ToString)
    Dim data As Integer = 2000
    Call WriteProcessMemory(pH, address, data, Len(data), 0&)

But, the WriteProcessMemory() returns 0, and value don't change

SOLUTION:

<DllImport("kernel32", CharSet:=CharSet.Auto, SetLastError:=True)> _
Private Shared Function WriteProcessMemory( _
                                      ByVal hProcess As IntPtr, _
                                      ByVal lpBaseAddress As IntPtr, _
                                      ByVal lpBuffer As Byte(), _
                                      ByVal nSize As UInt32, _
                                      ByRef lpNumberOfBytesWritten As UInt32 _
                                      ) As Boolean
End Function

        Dim proc() As Process = Process.GetProcessesByName("process")
        If proc.Length = 0 Then Console.WriteLine("Process Not Found") : Exit Sub
        Dim p = proc(0)
        Dim pH As Integer = OpenProcess(PROCESS_ALL_ACCESS, 0, p.Id)
        Dim address As Integer
        address = &H98544
        Dim memory As IntPtr
        Call ReadProcessMemory(pH, address, memory, 4, 0)
        Console.WriteLine(memory.ToString)
        Dim data() As Byte = BitConverter.GetBytes(2000)
        Call WriteProcessMemory(pH, address, data, 4, 0&)

Upvotes: 1

Views: 1843

Answers (2)

Cody Gray
Cody Gray

Reputation: 245012

When you're calling Windows API functions, you have to implement the error-checking code yourself. They won't throw exceptions when something goes wrong like the standard .NET Framework functions.

The specific error checks required vary depending on the function. (Inconsistency like this is precisely why .NET uses exceptions instead.) Checking the documentation is essential.

WriteProcessMemory works in a way that is fairly common. The return value is a BOOL (an integer that the Win32 libraries treat as a Boolean, where 0 = FALSE and 1 = TRUE). If the function fails, the return value is 0 (FALSE). If it's successful, the return value is 1 (TRUE):

If the function succeeds, the return value is nonzero.

If the function fails, the return value is 0 (zero). To get extended error information, call GetLastError. The function fails if the requested write operation crosses into an area of the process that is inaccessible.

The interesting part there, of course, is that we're told that we must call the GetLastError function in order to determine why the function failed. Part of that is true. You only call GetLastError when you're writing unmanaged C++ code. In .NET, you need to do something else. There are two steps:

  1. Mark the P/Invoke signature with SetLastError = True, which causes the CLR to automatically call GetLastError and save its value.
  2. Call Marshal.GetLastWin32Error to retrieve that value.

So, to debug it, you'll need to re-write your code like this (using the standard P/Invoke syntax, rather than the more limited Declare keyword that is unique to VB.NET):

<DllImport("kernel32", CharSet:=CharSet.Auto, SetLastError:=True)>
Private Shared Function WriteProcessMemory(
                                           ByVal hProcess As Integer, _
                                           ByVal lpBaseAddress As Integer, _
                                           ByVal lpBuffer As Integer, _
                                           ByVal nSize As Integer, _
                                           ByVal lpNumberOfBytesWritten As Integer _
                                          ) As Integer
End Function

Dim proc() As Process = Process.GetProcessesByName("process")
If proc.Length = 0 Then Console.WriteLine("Process Not Found") : Exit Sub
Dim p = proc(0)
Dim pH As Integer = OpenProcess(PROCESS_ALL_ACCESS, 0, p.Id)
Dim address As Integer
address = &H98544
Dim memory As IntPtr
Call ReadProcessMemory(pH, address, memory, 4, 0)
Console.WriteLine(memory.ToString)
Dim data As Integer = 2000
If WriteProcessMemory(pH, address, data, Len(data), 0&) = 0 Then
   ' The function failed, so find out what the error was
   MessageBox.Show("WriteProcessMemory failed with error " & Marshal.GetLastWin32Error)
End If

Once you know the error code, you can look it up in the list of System Error Codes to see what it means.


Turns out the error is number 299, ERROR_PARTIAL_COPY, which the documentation says just means:

Only part of a ReadProcessMemory or WriteProcessMemory request was completed.

Hmm, not very helpful... I was hoping you'd get something better. C'est la vie.

You're going to have to have a little bit of additional knowledge to solve that problem. A hint is given, once again, in the documentation. Any time it says that a parameter is a "pointer" (indicated in C++ syntax with an asterisk *), that means that in VB.NET, it needs to be passed as a reference, rather than as a value. To specify that something is passed as a reference, use the ByRef keyword. To pass by value, of course, you specify the default ByVal keyword.

Additionally, note that when the documentation says something is a handle or an address, you should declare it in VB.NET as an IntPtr type, rather than as an integer. IntPtr is special because its size changes, depending on whether the underlying operating system is 32-bit or 64-bit (unlike an integer, which is a fixed size, no matter what platform you're running on). Even though this is not causing your immediate problem, it will cause compatibility problems when you compile the code for 64-bit.

So it turns out that your declaration is actually wrong. Redeclare it like this:

<DllImport("kernel32", CharSet:=CharSet.Auto, SetLastError:=True)>
Private Shared Function WriteProcessMemory(
                                           ByVal hProcess As IntPtr, _
                                           ByVal lpBaseAddress As IntPtr, _
                                           ByVal lpBuffer As Integer, _
                                           ByVal nSize As Integer, _
                                           ByRef lpNumberOfBytesWritten As Integer _
                                          ) As Integer
End Function

You might also consider that the third parameter (lpBuffer) should be an array of bytes (Byte()), because it specifies the data to be written to the address space.

Upvotes: 2

David Heffernan
David Heffernan

Reputation: 613592

The documentation states:

If the function fails, the return value is 0 (zero). To get extended error information, call GetLastError. The function fails if the requested write operation crosses into an area of the process that is inaccessible.

I recommend you do what it says to find out why this has failed.

Having made that point, it seems likely that the cause of the failure is that the address 2000, which you are attempting to write to, is not a valid address in that process.

The lpBuffer parameter is a pointer and you are passing the integer value 2000 as that parameter. This is therefore interpreted and being a memory address. I suspect you actually intended to pass the address of data.

Upvotes: 1

Related Questions