Search code examples
vbawinapi64-bit

Is this 64 bit declaration of Virtual Protect correct?


Private Declare PtrSafe Function VirtualProtect Lib "kernel32" ( _
    ByVal memAddress As LongPtr, _
    ByVal lengthInBytes As LongPtr, _
    ByVal newProtect As protectFlags, _
    ByRef outOldProtect As protectFlags _
    ) As Long

Public Enum protectFlags 'an enum is a Long in VBA which is 32-bit so can be used in place of DWORD
    PAGE_NOACCESS = &H1
    PAGE_READONLY = &H2
    PAGE_READWRITE = &H4
    PAGE_WRITECOPY = &H8
    PAGE_EXECUTE = &H10
    PAGE_EXECUTE_READ = &H20
    PAGE_EXECUTE_READWRITE = &H40
    PAGE_EXECUTE_WRITECOPY = &H80
    PAGE_GUARD = &H100
    PAGE_NOCACHE = &H200
    PAGE_WRITECOMBINE = &H400
End Enum

I'm calling it like this:

Dim memoryAddress As LongPtr
memoryAddress = ... 'some protected memory

Dim originalProtection As protectFlags

If CBool(VirtualProtect(memoryAddress, 8, PAGE_EXECUTE_READWRITE, originalProtection)) Then
    Debug.Print "Previous protection cached: &H"; Hex(originalProtection)
Else
    Err.Raise 5, Description:="Memory address cannot be unprotected with PAGE_EXECUTE_READWRITE"
End If

... I'm then restoring the protection

VirtualProtect(memoryAddress, 8, originalProtection, temp) 

The problem is, originalProtection remains &H0 after the first call, and so in the second call I get LastDllError of 87 - "The parameter is incorrect.". I'm pretty sure the memory is protected originally since removing the call to VirtualProtect crashes my programme, whereas including it is successful. But I can't reset the protection.


For reference this is what I'm translating:

BOOL VirtualProtect(
  LPVOID lpAddress,
  SIZE_T dwSize,
  DWORD  flNewProtect,
  PDWORD lpflOldProtect
);

Solution

  • Yes the declaration is correct.

    The problem was that in my actual code, originalProtection was a public class variable.

    Public class variables cannot be passed ByRef to a function because they are actually implemented under the hood as Property Get/Let procedures with a hidden private variable, and so only a temporary value is passed to VirtualProtect rather than the underlying variable to be written to.

    The solution was to declare a local variable with Dim to receive the value and then to write it to my class variable.