首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在Go中调用内核32的ReadProcessMemory

在Go中调用内核32的ReadProcessMemory
EN

Stack Overflow用户
提问于 2015-10-02 05:32:15
回答 1查看 5.4K关注 0票数 5

我试图使用Go语言在Windows上操作进程,首先使用ReadProcessMemory读取其他进程的内存。

但是,对于大多数地址,我得到的Error: Only part of a ReadProcessMemory or WriteProcessMemory request was completed.错误。也许我的论点是错的,但我找不出原因。

有人能指出我在这里做错了什么吗?

代码语言:javascript
运行
复制
package main

import (
  "fmt"
)

import (
  windows "golang.org/x/sys/windows"
)

func main() {
  handle, _ := windows.OpenProcess(0x0010, false, 6100) // 0x0010 PROCESS_VM_READ, PID 6100
  procReadProcessMemory := windows.MustLoadDLL("kernel32.dll").MustFindProc("ReadProcessMemory")

  var data uint   = 0
  var length uint = 0

  for i := 0; i < 0xffffffff; i += 2 {
    fmt.Printf("0x%x\n", i)

    // BOOL ReadProcessMemory(HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead)
    ret, _, e := procReadProcessMemory.Call(uintptr(handle), uintptr(i), uintptr(data), 2, uintptr(length)) // read 2 bytes
    if (ret == 0) {
        fmt.Println("  Error:", e)          
    } else {
        fmt.Println("  Length:", length)
        fmt.Println("  Data:", data)                        
    }
  }

  windows.CloseHandle(handle)
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-10-02 11:20:13

uintptr(data)是不正确的:它从data (0类型为uint)获取值,并将其转换为unitptr类型--产生相同的值,转换为另一种类型产生的值,即x86上的空指针。

请注意,Go不是C,您不能真正地玩带有指针的脏游戏,更确切地说,您可以这样做,但只能通过使用unsafe内置包及其Pointer类型,这就像C中的void* (指向数据内存块中的某个地方)。

你需要的是

代码语言:javascript
运行
复制
import "unsafe"

var (
    data   [2]byte
    length uint32
)
ret, _, e := procReadProcessMemory.Call(uintptr(handle), uintptr(i),
    uintptr(unsafe.Pointer(&data[0])),
    2, uintptr(unsafe.Pointer(&length))) // read 2 bytes

观察这里所做的事情:

  1. 声明一个类型为“两个字节数组”的变量;
  2. 获取该数组的第一个元素的地址;
  3. 该地址是类型转换为类型unsafe.Pointer
  4. 然后将所获得的值转换为uintptr

之所以需要最后两个步骤,是因为Go具有垃圾收集功能:

  • 在Go中,当您在内存中获取一个值的地址并将其存储在一个变量中时,GC知道这个“隐式”指针,并且所使用的地址的值不会被垃圾收集--即使它变得不可到达,保存其地址的值是唯一的引用。
  • 即使使地址值丢失了它维护的类型信息(通过类型转换为unsafe.Pointer ),GC仍然会考虑新的值,并且行为类似于包含地址的“正常”值--正如上面所解释的。
  • 通过将该值转换为uintptr,可以使GC停止将其视为指针。因此,这种类型只适用于FFI/interop。 换句话说,在 := &data p := unsafe.Pointer(a) i := uintptr(p) 在data中只有三种对值的引用:变量本身、ap,而不是i

在处理外部代码调用时,您应该考虑这些规则,因为您永远不应该传递unitptr-typed值:它们只用于将数据封送到被调用的函数并将其解封处理返回,并且必须“当场”使用--与其类型相同的值--从/转换到。

还可以看到,在Go中,您不能只获取整数类型变量的地址,并将该地址提供给期望指向适当大小内存块的指针的函数。您必须处理字节数组,在被调用的函数写入数据之后,需要显式地将其转换为所需类型的值。这就是为什么Go中没有“类型转换”,而只有“类型转换”:您不能通过类型转换重新解释值的数据类型,对于FFI/interop来说,uintptr(unsafe.Pointer) (和back)是一个明显的例外,即使在这种情况下,您也可以将指针转换为指针,只需通过GC边界传输它。

要“序列化”和“反序列化”一个整数类型的值,您可以使用encoding/binary标准包或手摇无脑器简单函数,这些函数可以按位移位和or-s等。)

2015-10-05,根据James Henstridge的建议更新。

注意,函数返回后,ret表示没有错误,您必须检查length变量的值。

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/32901078

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档