Skip to content

Instantly share code, notes, and snippets.

@VityaSchel
Last active December 31, 2023 20:57
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save VityaSchel/32801695707420c2a22816a34a5b6cb5 to your computer and use it in GitHub Desktop.
Save VityaSchel/32801695707420c2a22816a34a5b6cb5 to your computer and use it in GitHub Desktop.
Example of reading another process memory in Go without knowing base address

You can find explanation here: https://stackoverflow.com/questions/71716646/golang-calculate-address-of-another-process-memory-based-on-process-handle-and-o/72674927#72674927 This is actually my answer posted by my friend because SO doesn't allow you to reclaim your own bounty :)

It's not perfect because I don't know Go and you can definetely replace windows.OpenProcess with kernel32.OpenProcess but I'm too tired to experiment. It just works!

Also keep in mind that this code only works if you have running TJoC:R game running, because I was writing it for my trainer https://github.com/VityaSchel/tJocer. You should change process name and addresses if you know how to do that. Otherwise, feel free to copy any part of code and use it.

module example.com/m/v2
go 1.18
require (
github.com/0xrawsec/golang-utils v1.3.0 // indirect
github.com/0xrawsec/golang-win32 v1.0.14 // indirect
github.com/Andoryuuta/kiwi v0.0.0-20200827010936-214591e6213d // indirect
github.com/TheTitanrain/w32 v0.0.0-20200114052255-2654d97dbd3d // indirect
golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c // indirect
golang.org/x/text v0.3.2 // indirect
)
github.com/0xrawsec/golang-utils v1.3.0 h1:fMgwKu5M2PXFwEfwN9B2T1bfg7LPCaV9fL6Xs/nf2Ps=
github.com/0xrawsec/golang-utils v1.3.0/go.mod h1:DADTtCFY10qXjWmUVhhJqQIZdSweaHH4soYUDEi8mj0=
github.com/0xrawsec/golang-win32 v1.0.14 h1:Lj45Cd7qnhCbtnrNCBI3twefRVh759q/rDXrutxQQOo=
github.com/0xrawsec/golang-win32 v1.0.14/go.mod h1:LDGq8VzCwLZccK1qg7oKBc8n5DmPLi79w+wjew1UApg=
github.com/Andoryuuta/kiwi v0.0.0-20200827010936-214591e6213d h1:Jnu1j015U0uK8i0/FQLeHd+ZJTpPKycMydY3xwcXe/I=
github.com/Andoryuuta/kiwi v0.0.0-20200827010936-214591e6213d/go.mod h1:mnEJPG4BErdGQtRMLZnRJK4ts2ZDgtZ0lstYBpP55+M=
github.com/TheTitanrain/w32 v0.0.0-20200114052255-2654d97dbd3d h1:2xp1BQbqcDDaikHnASWpVZRjibOxu7y9LhAv04whugI=
github.com/TheTitanrain/w32 v0.0.0-20200114052255-2654d97dbd3d/go.mod h1:peYoMncQljjNS6tZwI9WVyQB3qZS6u79/N3mBOcnd3I=
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.10.0/go.mod h1:NxmoDg/QLVWluQDUYG7XBZTLUpKeFa8e3aMf1BfjyHk=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c h1:aFV+BgZ4svzjfabn8ERpuB4JI4N6/rdy1iusx77G3oU=
golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190320215829-36c10c0a621f/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190625160430-252024b82959/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
package main
import (
"encoding/binary"
"fmt"
"math"
"path/filepath"
"strconv"
"unsafe"
"github.com/0xrawsec/golang-win32/win32"
kernel32 "github.com/0xrawsec/golang-win32/win32/kernel32"
windows "golang.org/x/sys/windows"
)
var handle windows.Handle
var procReadProcessMemory *windows.Proc
var baseAddress int64
func memoryReadInit(pid uint32) (int64, bool) {
handle, _ = windows.OpenProcess(0x0010 | windows.PROCESS_VM_READ | windows.PROCESS_QUERY_INFORMATION, false, pid)
procReadProcessMemory = windows.MustLoadDLL("kernel32.dll").MustFindProc("ReadProcessMemory")
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)
baseAddress = int64(info.LpBaseOfDll)
return baseAddress, true
}
}
return 0, false
}
func memoryReadClose() {
windows.CloseHandle(handle)
}
func readMemoryAt(address int64) float32 {
var (
data [4]byte
length uint32
)
procReadProcessMemory.Call(
uintptr(handle),
uintptr(address),
uintptr(unsafe.Pointer(&data[0])),
uintptr(len(data)),
uintptr(unsafe.Pointer(&length)),
)
bits := binary.LittleEndian.Uint32(data[:])
float := math.Float32frombits(bits)
return float
}
func readMemoryAtByte8(address int64) uint64 {
var (
data [8]byte
length uint32
)
procReadProcessMemory.Call(
uintptr(handle),
uintptr(address),
uintptr(unsafe.Pointer(&data[0])),
uintptr(len(data)),
uintptr(unsafe.Pointer(&length)),
)
byte8 := binary.LittleEndian.Uint64(data[:])
return byte8
}
type staticPointer struct {
baseOffset int64
offsets []string
}
func GetAddresses() (int64, int64) {
xPositionPointer := staticPointer{0x2518790, []string{"2E4", "10", "8", "8", "8", "78", "5E0"}}
zPositionPointer := staticPointer{0x2518790, []string{"2E8", "10", "8", "8", "8", "78", "5E0"}}
xPositionAddress := calculateAddress(xPositionPointer)
zPositionAddress := calculateAddress(zPositionPointer)
xPositionAddressInt, _ := strconv.ParseInt(xPositionAddress, 16, 0)
zPositionAddressInt, _ := strconv.ParseInt(zPositionAddress, 16, 0)
return xPositionAddressInt, zPositionAddressInt
}
func calculateAddress(pointer staticPointer) string {
startingPointer := baseAddress + pointer.baseOffset
startingAddress := readMemoryAtByte8(startingPointer)
var value string = strconv.FormatInt(int64(startingAddress), 16)
for i := len(pointer.offsets)-1; i >= 0; i-- {
offset := pointer.offsets[i]
addressPointer := sumHex(value, offset)
if(i > 0) {
addressInt, _ := strconv.ParseInt(addressPointer, 16, 64)
nextAddressDecimal := readMemoryAtByte8(addressInt)
value = strconv.FormatInt(int64(nextAddressDecimal), 16)
} else {
value = addressPointer
}
}
return value
}
func sumHex(aHex string, bHex string) string {
aDecimal, _ := strconv.ParseInt(aHex, 16, 0)
bDecimal, _ := strconv.ParseInt(bHex, 16, 0)
resultDecimal := aDecimal + bDecimal
resultHex := strconv.FormatInt(resultDecimal, 16)
return resultHex
}
func main() {
pid, _ := bindDefaultProcess("UE4Game-Win64-Shipping.exe")
baseAddress, _ := memoryReadInit(pid)
fmt.Println("Base address is", baseAddress)
xPositionAddressInt, zPositionAddressInt := GetAddresses()
fmt.Println("X address is", strconv.FormatInt(xPositionAddressInt, 16), "Z address is", strconv.FormatInt(zPositionAddressInt, 16))
x := readMemoryAt(xPositionAddressInt)
z := readMemoryAt(zPositionAddressInt)
fmt.Println("X="+fmt.Sprintf("%g", x), "Z="+fmt.Sprintf("%g", z))
}
package main
import (
"unsafe"
"syscall"
"strings"
windows "golang.org/x/sys/windows"
)
const TH32CS_SNAPPROCESS = 0x00000002
type WindowsProcess struct {
ProcessID int
ParentProcessID int
Exe string
}
func processes() ([]WindowsProcess, error) {
handle, err := windows.CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)
if err != nil {
return nil, err
}
defer windows.CloseHandle(handle)
var entry windows.ProcessEntry32
entry.Size = uint32(unsafe.Sizeof(entry))
err = windows.Process32First(handle, &entry)
if err != nil {
return nil, err
}
results := make([]WindowsProcess, 0, 50)
for {
results = append(results, newWindowsProcess(&entry))
err = windows.Process32Next(handle, &entry)
if err != nil {
if err == syscall.ERROR_NO_MORE_FILES {
return results, nil
}
return nil, err
}
}
}
func findProcessByName(processes []WindowsProcess, name string) *WindowsProcess {
for _, p := range processes {
if strings.ToLower(p.Exe) == strings.ToLower(name) {
return &p
}
}
return nil
}
func newWindowsProcess(e *windows.ProcessEntry32) WindowsProcess {
end := 0
for {
if e.ExeFile[end] == 0 {
break
}
end++
}
return WindowsProcess{
ProcessID: int(e.ProcessID),
ParentProcessID: int(e.ParentProcessID),
Exe: syscall.UTF16ToString(e.ExeFile[:end]),
}
}
func bindDefaultProcess(defaultName string) (uint32, bool) {
procs, err := processes()
if err != nil {
return 0, false
}
explorer := findProcessByName(procs, defaultName)
if explorer == nil {
return 0, false
}
return uint32(explorer.ProcessID), true
}
@pedro-walter
Copy link

Hello there,

I'm using this code, with some adaptations, to read the memory of a SNES emulator. I want to publish it as a module one can import into their project, giving you credit of couse. Would you be okay with it?

@VityaSchel
Copy link
Author

Hello there,

I'm using this code, with some adaptations, to read the memory of a SNES emulator. I want to publish it as a module one can import into their project, giving you credit of couse. Would you be okay with it?

Yes, of course use it! Make sure to leave a link to your project here in the comments :)

@pedro-walter
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment