VB读取内存地址时输出异常,如何正确传入目标地址?
我编写了如下VB代码来读取GeometryDash进程的内存:
Imports System.Runtime.InteropServices Imports System.Text Public Class Form1 Const PROCESS_WM_READ As Integer = &H10 <DllImport("kernel32.dll")> Public Shared Function OpenProcess(dwDesiredAccess As Integer, bInheritHandle As Boolean, dwProcessId As Integer) As IntPtr End Function <DllImport("kernel32.dll")> Public Shared Function ReadProcessMemory(hProcess As Integer, lpBaseAddress As Integer, lpBuffer As Byte(), dwSize As Integer, ByRef lpNumberOfBytesRead As Integer) As Boolean End Function Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load Dim notepadProcess As Process = Process.GetProcessesByName("GeometryDash")(0) Dim processHandle As IntPtr = OpenProcess(PROCESS_WM_READ, False, notepadProcess.Id) Dim bytesRead As Integer = 0 Dim buffer As Byte() = New Byte(23) {} 'The address in this line is hard-coded. Use whatever is appropriate for your situation. ReadProcessMemory(CInt(processHandle), 117317672, buffer, buffer.Length, bytesRead) MsgBox(Encoding.Unicode.GetString(buffer)) End Sub End Class
运行后输出2-3个奇怪的日文字符,其中传入的地址“117317672”是十六进制地址“06FE2028”的十进制形式。我认为问题出在地址的传入方式上,请问该如何正确传入内存地址?
你遇到的核心问题是内存地址的类型不匹配,再加上编码和函数签名的小问题,咱们一步步来解决:
1. 先修正ReadProcessMemory的函数签名
你的DllImport声明有两个关键错误:
hProcess应该是IntPtr类型,而不是Integer——进程句柄是指针类型,64位系统下用Integer会直接截断数据,导致句柄无效lpBaseAddress也得是IntPtr类型,32位和64位进程的内存地址宽度不一样,用IntPtr能完美兼容两种架构
另外建议加上SetLastError:=True,这样后续可以通过Marshal.GetLastWin32Error()获取系统错误码,调试起来方便很多。修正后的声明:
<DllImport("kernel32.dll", SetLastError:=True)> Public Shared Function ReadProcessMemory( hProcess As IntPtr, lpBaseAddress As IntPtr, lpBuffer As Byte(), dwSize As Integer, ByRef lpNumberOfBytesRead As Integer ) As Boolean End Function
2. 正确传入内存地址
你手动把十六进制转十进制再传的方式容易出错,尤其是地址长度超过Integer范围的时候。直接用VB的十六进制字面量(前缀&H)初始化IntPtr就好:
' 直接用你的十六进制地址初始化,不用转十进制 Dim targetAddress As IntPtr = New IntPtr(&H06FE2028)
如果你的地址是字符串形式,也可以用IntPtr.Parse指定十六进制格式来处理:
Dim targetAddress As IntPtr = IntPtr.Parse("06FE2028", Globalization.NumberStyles.HexNumber)
之后调用ReadProcessMemory时,直接传入这个targetAddress就行,不用再做类型转换。
3. 验证编码和读取长度
乱码也可能是编码不匹配导致的——你现在用Encoding.Unicode.GetString(buffer),但要确认目标内存里的数据确实是UTF-16编码。如果是ASCII或UTF-8,用Unicode解码肯定会出乱码。
可以同时尝试几种编码来测试,找到正确的那一个:
Dim unicodeResult As String = Encoding.Unicode.GetString(buffer, 0, bytesRead) Dim utf8Result As String = Encoding.UTF8.GetString(buffer, 0, bytesRead) Dim asciiResult As String = Encoding.ASCII.GetString(buffer, 0, bytesRead)
另外注意读取时只用实际读到的字节数(bytesRead)来解码,不要用整个buffer的长度,避免末尾的空字节干扰。
4. 加上调试检查
每次调用ReadProcessMemory后,先检查返回值是否为True,如果失败就获取错误码,能快速定位权限不足、地址无效这类问题:
Dim readSuccess As Boolean = ReadProcessMemory(processHandle, targetAddress, buffer, buffer.Length, bytesRead) If Not readSuccess Then Dim errorCode As Integer = Marshal.GetLastWin32Error() MsgBox($"读取失败,错误码:{errorCode}") End If
修正后的完整代码示例
Imports System.Runtime.InteropServices Imports System.Text Imports System.Diagnostics Imports System.Globalization Public Class Form1 Const PROCESS_VM_READ As Integer = &H10 <DllImport("kernel32.dll")> Public Shared Function OpenProcess(dwDesiredAccess As Integer, bInheritHandle As Boolean, dwProcessId As Integer) As IntPtr End Function <DllImport("kernel32.dll", SetLastError:=True)> Public Shared Function ReadProcessMemory( hProcess As IntPtr, lpBaseAddress As IntPtr, lpBuffer As Byte(), dwSize As Integer, ByRef lpNumberOfBytesRead As Integer ) As Boolean End Function Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load Try Dim gdProcess As Process = Process.GetProcessesByName("GeometryDash")(0) Dim processHandle As IntPtr = OpenProcess(PROCESS_VM_READ, False, gdProcess.Id) If processHandle = IntPtr.Zero Then MsgBox("无法打开进程,可能是权限不足或进程未运行") Return End If Dim bytesRead As Integer = 0 Dim buffer As Byte() = New Byte(23) {} ' 正确初始化目标内存地址 Dim targetAddress As IntPtr = New IntPtr(&H06FE2028) Dim readSuccess As Boolean = ReadProcessMemory(processHandle, targetAddress, buffer, buffer.Length, bytesRead) If Not readSuccess Then Dim errorCode As Integer = Marshal.GetLastWin32Error() MsgBox($"读取内存失败,错误码:{errorCode}") Else ' 尝试多种编码,找到正确结果 Dim unicodeResult As String = Encoding.Unicode.GetString(buffer, 0, bytesRead) Dim utf8Result As String = Encoding.UTF8.GetString(buffer, 0, bytesRead) Dim asciiResult As String = Encoding.ASCII.GetString(buffer, 0, bytesRead) MsgBox($"Unicode结果:{unicodeResult}{vbCrLf}UTF8结果:{utf8Result}{vbCrLf}ASCII结果:{asciiResult}") End If Catch ex As Exception MsgBox($"发生异常:{ex.Message}") End Try End Sub End Class
内容的提问来源于stack exchange,提问作者NexusEyes

