Created
June 12, 2025 19:36
-
-
Save galoryber/88c232e24bfe3451905cdc8dd88db3e8 to your computer and use it in GitHub Desktop.
Golang memory editor PoC
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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