Skip to content

Instantly share code, notes, and snippets.

@galoryber
Created June 12, 2025 19:36
Show Gist options
  • Save galoryber/88c232e24bfe3451905cdc8dd88db3e8 to your computer and use it in GitHub Desktop.
Save galoryber/88c232e24bfe3451905cdc8dd88db3e8 to your computer and use it in GitHub Desktop.
Golang memory editor PoC
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
// readFunctionBytes resolves a function's address in a DLL, reads bytes from that location,
// and returns the byte slice.
func readFunctionBytes(dllName, functionName string, startIndex, numBytes int) ([]byte, error) {
// Load the DLL
dll, err := syscall.LoadLibrary(dllName)
if err != nil {
return nil, fmt.Errorf("failed to load DLL: %w", err)
}
//defer syscall.FreeLibrary(dll)
// Get the address of the function
funcAddr, err := syscall.GetProcAddress(dll, functionName)
if err != nil {
return nil, fmt.Errorf("failed to find function %s: %w", functionName, err)
}
// Ensure valid offset and length
if startIndex < 0 || numBytes <= 0 {
return nil, fmt.Errorf("invalid startIndex or numBytes")
}
// Convert the function address to a pointer
addr := uintptr(funcAddr) + uintptr(startIndex)
// Read bytes from the memory location
bytes := make([]byte, numBytes)
for i := 0; i < numBytes; i++ {
bytes[i] = *(*byte)(unsafe.Pointer(addr + uintptr(i)))
}
return bytes, nil
}
// writeFunctionBytes writes a given byte slice to the specified function's memory location in a DLL.
func writeFunctionBytes(dllName, functionName string, startIndex int, bytesToWrite []byte) error {
// Load the DLL
dll, err := syscall.LoadLibrary(dllName)
if err != nil {
return fmt.Errorf("failed to load DLL: %w", err)
}
//defer syscall.FreeLibrary(dll)
// Get the address of the function
funcAddr, err := syscall.GetProcAddress(dll, functionName)
if err != nil {
return fmt.Errorf("failed to find function %s: %w", functionName, err)
}
// Ensure valid offset
if startIndex < 0 {
return fmt.Errorf("invalid startIndex")
}
// Convert the function address to a pointer
addr := uintptr(funcAddr) + uintptr(startIndex)
// Change memory protection to allow writing
var oldProtect uint32
var lessOldProtect uint32
size := uintptr(len(bytesToWrite))
if err := windows.VirtualProtect(addr, size, windows.PAGE_EXECUTE_READWRITE, &oldProtect); err != nil {
return fmt.Errorf("failed to change memory protection: %w", err)
}
success := false
defer func() {
if success {
windows.VirtualProtect(addr, size, oldProtect, &lessOldProtect)
}
}()
// Write bytes to the memory location
for i, b := range bytesToWrite {
*(*byte)(unsafe.Pointer(addr + uintptr(i))) = b
}
success = true
return nil
}
func main() {
scanner := bufio.NewScanner(os.Stdin)
for {
fmt.Println("Enter DLL name (or 'exit' to quit):")
scanner.Scan()
dllName := strings.TrimSpace(scanner.Text())
if dllName == "exit" {
break
}
fmt.Println("Enter function name:")
scanner.Scan()
functionName := strings.TrimSpace(scanner.Text())
fmt.Println("Enter starting index:")
scanner.Scan()
startIndex, err := strconv.Atoi(strings.TrimSpace(scanner.Text()))
if err != nil {
fmt.Printf("Invalid starting index: %v\n", err)
continue
}
fmt.Println("Would you like to read or write bytes? (type 'read' or 'write')")
scanner.Scan()
operation := strings.TrimSpace(scanner.Text())
if operation == "read" {
fmt.Println("Enter number of bytes to read:")
scanner.Scan()
numBytes, err := strconv.Atoi(strings.TrimSpace(scanner.Text()))
if err != nil {
fmt.Printf("Invalid number of bytes: %v\n", err)
continue
}
// Call the function
bytes, err := readFunctionBytes(dllName, functionName, startIndex, numBytes)
if err != nil {
fmt.Printf("Error: %v\n", err)
continue
}
// Convert bytes to hexadecimal string
hexBytes := make([]string, len(bytes))
for i, b := range bytes {
hexBytes[i] = fmt.Sprintf("0x%02X", b)
}
fmt.Printf("Bytes at %s!%s: %s\n", dllName, functionName, strings.Join(hexBytes, ", "))
} else if operation == "write" {
fmt.Println("Enter bytes to write (hexadecimal, comma-separated, e.g., 0x10,0x20,0x30):")
scanner.Scan()
byteStr := strings.TrimSpace(scanner.Text())
byteParts := strings.Split(byteStr, ",")
bytesToWrite := make([]byte, len(byteParts))
for i, part := range byteParts {
// Remove "0x" prefix if present and parse as hexadecimal
part = strings.TrimPrefix(strings.TrimSpace(part), "0x")
val, err := strconv.ParseUint(part, 16, 8)
if err != nil {
fmt.Printf("Invalid hex byte value: %v\n", err)
continue
}
bytesToWrite[i] = byte(val)
}
// Call the function
err := writeFunctionBytes(dllName, functionName, startIndex, bytesToWrite)
if err != nil {
fmt.Printf("Error: %v\n", err)
continue
}
fmt.Printf("Successfully wrote bytes to %s!%s\n", dllName, functionName)
} else {
fmt.Println("Invalid operation. Please type 'read' or 'write'.")
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment