Skip to content

Instantly share code, notes, and snippets.

@ks888
Created July 21, 2018 05:09
Show Gist options
  • Save ks888/cfa7b3488e63945cc8ad2e02356b7e8f to your computer and use it in GitHub Desktop.
Save ks888/cfa7b3488e63945cc8ad2e02356b7e8f to your computer and use it in GitHub Desktop.
Dynamic code generation example in Golang (Linux, x86_64)
package main
import (
"fmt"
"log"
"reflect"
"syscall"
"unsafe"
)
var helloworldInstructions = []byte{
0x48, 0x83, 0xec, 0x48, // sub $0x48,%rsp
0x48, 0x89, 0x6c, 0x24, 0x40, // mov %rbp,0x40(%rsp)
0x48, 0x8d, 0x6c, 0x24, 0x40, // lea 0x40(%rsp),%rbp
0x48, 0x8d, 0x05, 0x00, 0x00, 0x00, 0x00, // lea 0x0(%rip),%rax # string type address
0x48, 0x89, 0x44, 0x24, 0x30, // mov %rax,0x30(%rsp)
0x48, 0x8d, 0x05, 0x2f, 0x00, 0x00, 0x00, // lea 0x2f(%rip),%rax # string value address
0x48, 0x89, 0x44, 0x24, 0x38, // mov %rax,0x38(%rsp)
0x48, 0x8d, 0x44, 0x24, 0x30, // lea 0x30(%rsp),%rax
0x48, 0x89, 0x04, 0x24, // mov %rax,(%rsp)
0x48, 0xc7, 0x44, 0x24, 0x08, 0x01, 0x00, 0x00, 0x00, // movq $0x1,0x8(%rsp)
0x48, 0xc7, 0x44, 0x24, 0x10, 0x01, 0x00, 0x00, 0x00, // movq $0x1,0x10(%rsp)
0xe8, 0x00, 0x00, 0x00, 0x00, // callq 0x0(%rip) # fmt.Println
0x48, 0x8b, 0x6c, 0x24, 0x40, // mov 0x40(%rsp),%rbp
0x48, 0x83, 0xc4, 0x48, // add $0x48,%rsp
0xc3, // retq
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // # points to data
0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // # data length
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, // "Hello world"
}
func createHelloworldFunction() func() {
region, err := anonMMap(len(helloworldInstructions))
if err != nil {
log.Fatalf("failed to mmap: %+v", err)
}
copy(region, helloworldInstructions)
regionAddr := *(*uintptr)(unsafe.Pointer(&region))
strTypeAddr := stringTypeAddr()
relAddr := strTypeAddr - (regionAddr + 21)
region[17] = byte(relAddr)
region[18] = byte(relAddr >> 8)
region[19] = byte(relAddr >> 16)
region[20] = byte(relAddr >> 24)
printlnFunc := fmt.Println
printlnAddr := **(**uintptr)(unsafe.Pointer(&printlnFunc))
relAddr = printlnAddr - (regionAddr + 70)
region[66] = byte(relAddr)
region[67] = byte(relAddr >> 8)
region[68] = byte(relAddr >> 16)
region[69] = byte(relAddr >> 24)
helloworldDataAddr := (regionAddr + 96)
region[80] = byte(helloworldDataAddr)
region[81] = byte(helloworldDataAddr >> 8)
region[82] = byte(helloworldDataAddr >> 16)
region[83] = byte(helloworldDataAddr >> 24)
region[84] = byte(helloworldDataAddr >> 32)
region[85] = byte(helloworldDataAddr >> 40)
region[86] = byte(helloworldDataAddr >> 48)
region[87] = byte(helloworldDataAddr >> 56)
pointerToFunc := (uintptr)(unsafe.Pointer(&region))
return *(*func())(unsafe.Pointer(&pointerToFunc))
}
func anonMMap(len int) ([]byte, error) {
flags := syscall.MAP_PRIVATE | syscall.MAP_ANON | syscall.MAP_32BIT
prot := syscall.PROT_READ | syscall.PROT_WRITE | syscall.PROT_EXEC
return syscall.Mmap(0, 0, len, prot, flags)
}
func munmap(addr, len int) error {
_, _, errno := syscall.Syscall(syscall.SYS_MUNMAP, uintptr(addr), uintptr(len), 0)
if errno != 0 {
return syscall.Errno(errno)
}
return nil
}
func stringTypeAddr() uintptr {
var str string
efaceToString := reflect.TypeOf(str)
collapsedEfaceToString := *(*struct {
typ uintptr
val uintptr
})(unsafe.Pointer(&efaceToString))
return collapsedEfaceToString.val
}
func main() {
helloworld := createHelloworldFunction()
helloworld()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment