基于进程句柄和偏移量计算另一个进程内存的地址(使用 Golang)

3
我需要从另一个进程的内存中读取值,需要给定进程名称/ID、指针和偏移量,并实时将其输出给用户。我已经找到了另一个问题并成功实现了逻辑,可以从进程内存的给定地址读取和转换浮点值,但由于每次重新加入游戏时地址都会改变,所以我需要计算它。我已经使用作弊引擎找到了指针和进程偏移量:

enter image description here

根据我阅读的理论,需要使用以下算法计算最终地址:
  1. 使用常量偏移量(+02518790)计算基地址(1DC45993300)
  2. 将该地址与指针值相加
  3. 从底部(5E0)到顶部(2E4)重复第2步骤中的操作
  4. 这是您可以用来检索数据的最终地址
我对第2、3和4步没有问题:我可以在地址上读取进程内存求和十六进制数,但我不知道如何使用偏移量计算第一个地址,在Go中应该怎么做?我正在尝试在Windows 10上完成此操作。
更新:我尝试了这个问题中的代码,但返回的基地址不正确。我得到的是7ff611530000,而Cheat Engine中的实际基地址是1A009A6AB70。

更新2:可能很难理解我想做什么,所以我添加了另一张图片。我需要找到如何进行“加”操作以及如何在红色方框中找到地址(绿色可以计算,紫色是常数)。

enter image description here


1
通常情况下,您无法读取或写入其他进程的内存。操作系统通常会为调试器提供一种方法来执行此操作,但是这是通过在其他进程中使用地址进行的,由操作系统确定您是否有权首先执行此操作,然后再执行它(如果其他进程的内存此时已分页,则可能涉及分页)。 - torek
1
@torek,读写其他进程内存是可能的,并且Cheat Engine可以实现。 - Sherif Elmetainy
@SherifElmetainy:完全绕过安全性,因此操作系统限制谁可以在何时执行此操作。如果操作系统未能通过此限制过程,则会出现CVE。所有这些都远远超出了正常Go编程的范围。 - torek
@torek 作弊程序是如何工作的?十六进制编辑器呢?我不介意将任何其他语言连接到Go中,只是为了读取基地址。事实上,我已经完成了其他所有工作:我可以实时读取其他进程的内存,我可以查找值并转换它们,常量也可以正常工作,当我放入Cheat Engine的基地址时。我只需要找到一种获取进程基地址的方法,我怀疑它不会比按精确地址读取值更受限制,如果我错了,请纠正我 :) - VityaSchel
1
请注意,就像您链接的答案中一样,您可能需要偶尔使用一些技巧来避免Go的GC处理某些数据。 Go运行时中的syscall包也有这些技巧,原因相同。(运行时充满了这种奇怪的技巧。) - torek
显示剩余3条评论
1个回答

1
为了查找另一个进程的地址并读取其值,您必须根据进程的偏移量和基地址进行计算。Cheat Engine将读取地址值操作显示为 [hex + hex] -> 地址在指针编辑器中。因此,每次看到 [address + offset] -> 下一个地址时,它意味着将地址和偏移量作为十六进制(16)相加,并在进程内存中的该地址处读取值。检索到的值是下一个地址,您应该使用它来获取以下一个地址。重复此过程,直到到达最后一个偏移量,然后只需将地址和偏移量相加而不读取值。结果的地址就是存储值的位置。
如何找到基地址?虽然在 Cheat Engine 中它可能看起来是常数(如果您将 0 替换为 02518790,则每次重新启动进程时都会得到相同的地址),但它只是一个虚拟地址,请勿使用它。相反,使用 winapi 使用 EnumProcessModules 迭代指定进程的所有模块。您可以通过窗口标题搜索正在运行的应用程序以查找 PID。将模块的文件名与 GetModuleFilenameExW 进行比较。当您找到带有恒定文件名(在您的情况下为“UE4Game-Win64-Shipping.exe”)的模块时,请使用 GetModuleInformation 检索 LpBaseOfDll。而不是 EntryPoint,EntryPoint 不是基地址。
现在,您已经拥有LpBaseOfDll,请向其添加常量偏移量(02518790),并读取结果地址处的值。这是您应该用于运行循环和添加偏移量的起始地址。因此,图像标记为“加法操作”的是LpBaseOfDll和偏移量的总和。实际上,Cheat Engine只接受可执行文件名而不是偏移量,尝试将“kernel32.dll”放入地址字段中即可:)
要与虚拟内存交互,您必须使用Windows本机API(kernel32.dll)。与任何其他语言一样,Go具有winapi的包装器。您可以在传统的硬编码“golang.org/x/sys/windows”,现代和实验性的“github.com/Andoryuuta/kiwi”之间进行选择,但我建议您使用“github.com/0xrawsec/golang-win32/win32/kernel32”。
下面的代码演示了如何获取基地址。我发布了一个GitHub gist,其中包含可以通过名称查找进程ID并读取float32值的完整代码。
package main

import (
    "fmt"
    "path/filepath"

    "github.com/0xrawsec/golang-win32/win32"
    kernel32 "github.com/0xrawsec/golang-win32/win32/kernel32"
    windows "golang.org/x/sys/windows"
)

func memoryReadInit(pid uint32) (int64, bool) {
    win32handle, _ := kernel32.OpenProcess(0x0010 | windows.PROCESS_VM_READ | windows.PROCESS_QUERY_INFORMATION, win32.BOOL(0), win32.DWORD(pid))
    moduleHandles, _ := kernel32.EnumProcessModules(win32handle)
    for _, moduleHandle := range moduleHandles {
        s, _ := kernel32.GetModuleFilenameExW(win32handle, moduleHandle)
        targetModuleFilename := "UE4Game-Win64-Shipping.exe"
        if(filepath.Base(s) == targetModuleFilename) {
            info, _ := kernel32.GetModuleInformation(win32handle, moduleHandle)
            return int64(info.LpBaseOfDll), true
        }
    }
    return 0, false
}

func main() {
    var pid uint32 = 0x38E4 // put PID here, you can find it in Cheat Engine process list
    baseAddress, _ := memoryReadInit(pid)
    fmt.Println("Base address is", baseAddress)
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接