众所周知,go是一款天生多线程的语言,一般用来作为服务端开发,但是并不代表它只能作为服务端开发,我们还可以用来开发一些桌面端小工具。
今天我们将使用windows经典游戏《扫雷》,来作为我们的修改对象,通过go去修改扫雷游戏运行时内存里面的数值。
首先,我们运行扫雷
好的,游戏成功启动,我们打开著名的CE修改器(Cheat Engine)
然后找到扫雷这个进程,并打开它
成功打开之后我们查看现在的“雷数”,可以看到,现在是40个雷,我们选择扫描类型精确数值,然后输入40,然后点击首次扫描
找到一些地址
此时我们先不着急,我们切换点击扫雷左上角的“游戏”按钮,然后切换初级,此时“雷数”变为了10,我们把数值改成10,然后点击再次扫描
此时我们精确到3个基址
我们可以通过鼠标右键点击扫雷,然后标记雷区,这时我们发现第一个基址发生了变化,变成了9,于是我们就找到了这个雷数的基址
我们双击第一个地址,然后下方出现的16进制的地址也就是我们最终需要修改的基址 0x01005194 (注:0x是用来表示16进制的)
我们发现通过CE可以直接去进行修改,但是这样会造成我们每次都需要打开CE去进行扫描数值,比较麻烦,怎么办呢,这就到我们的第二步,打开我们的golang进行编码
1.添加自动获取pid的方法,这样就不用每次都手动选择程序的pid了
func getPID(name string) (pid int) {
// 查找指定名称的进程ID
cmd := exec.Command("tasklist", "/NH", "/FI", "IMAGENAME eq "+name)
output, err := cmd.Output()
if err != nil {
fmt.Println("command error:", err)
return
}
lines := strings.Split(string(output), "\r\n")
for _, line := range lines {
if strings.TrimSpace(line) == "" {
continue
}
fields := strings.Fields(line)
pid, err = strconv.Atoi(fields[1])
if err == nil {
fmt.Println("pid 获取成功", pid)
return
}
}
return
}
2.完整代码实现
package main
import (
"fmt"
"golang.org/x/sys/windows"
"os/exec"
"strconv"
"strings"
"unsafe"
)
const (
AppName = "winmine.exe" // 应用名称
Addr = uintptr(0x01005194) // 需要修改的基址
NewValue = 66 // 新的值
)
func main() {
pid := getPID(AppName)
// 打开进程,获取进程句柄 windows.PROCESS_ALL_ACCESS = 0x000F0000|0x00100000|0xFFF
handle, err := windows.OpenProcess(0x000F0000|0x00100000|0xFFF, false, uint32(pid))
if err != nil {
fmt.Println("OpenProcess error:", err)
return
}
defer windows.CloseHandle(handle)
// 读取进程内存中的值
var value int32
var nread uintptr
err = windows.ReadProcessMemory(handle, Addr, (*byte)(unsafe.Pointer(&value)), 4, &nread)
if err != nil {
fmt.Println("read process memory error:", err)
return
}
fmt.Println("原始值:", value)
value = int32(NewValue) // 需要写入的值
var nwrite uintptr
err = windows.WriteProcessMemory(handle, Addr, (*byte)(unsafe.Pointer(&value)), 4, &nwrite)
if err != nil {
fmt.Println("write process memory error:", err)
return
}
fmt.Println("修改后的值:", value)
}
func getPID(name string) (pid int) {
// 查找指定名称的进程ID
cmd := exec.Command("tasklist", "/NH", "/FI", "IMAGENAME eq "+name)
output, err := cmd.Output()
if err != nil {
fmt.Println("command error:", err)
return
}
lines := strings.Split(string(output), "\r\n")
for _, line := range lines {
if strings.TrimSpace(line) == "" {
continue
}
fields := strings.Fields(line)
pid, err = strconv.Atoi(fields[1])
if err == nil {
fmt.Println("pid 获取成功", pid)
return
}
}
return
}
上述代码通过应用名自动去查找pid,然后打开对应的句柄,去修改我们找到的基址的数值,上面代码只是一个示例,实际应用可以添加GUI,可以去动态的输入我们需要修改的数值
最终的运行结果如下
本文/资源仅供学习交流,请勿用于任何违法用途