func fset(target, old, new uintptr) { pageOffset := target % pageSize pageAddr := target - pageOffset var mem []byte memh := (*reflect.SliceHeader)(unsafe.Pointer(&mem)) memh.Data = pageAddr memh.Len = pageSize * 2 memh.Cap = pageSize * 2 oldAddr := make([]byte, 8) newAddr := make([]byte, 8) binary.LittleEndian.PutUint64(oldAddr, uint64(old)) binary.LittleEndian.PutUint64(newAddr, uint64(new)) // BSD's syscall package misses Mprotect. Use cgo instead. C.mprotect(unsafe.Pointer(pageAddr), C.size_t(len(mem)), protEXEC|protREAD|protWRITE) defer C.mprotect(unsafe.Pointer(pageAddr), C.size_t(len(mem)), protEXEC|protREAD) delta := make([]byte, 4) for i, c := range mem[pageOffset:] { if c == 0xe8 && int(pageOffset)+i+5 < len(mem) { instrAddr := pageAddr + pageOffset + uintptr(i) binary.LittleEndian.PutUint32(delta, uint32(old-instrAddr-5)) if bytes.Equal(mem[int(pageOffset)+i+1:int(pageOffset)+i+5], delta) { binary.LittleEndian.PutUint32(mem[int(pageOffset)+i+1:], uint32(new-instrAddr-5)) return } } } panic("cannot setup qml package for testing") }
//export stub func stub(ptrxx unsafe.Pointer) { ptr := uintptr(ptrxx) var data []byte header := (*reflect.SliceHeader)(unsafe.Pointer(&data)) header.Data = ptr header.Cap = 10 header.Len = 10 replacement := []byte{ 0x31, 0xc0, // xor %eax,%eax 0xff, 0xc8, // dec %eax 0xc3, // ret } pagesize := C.sysconf(C._SC_PAGE_SIZE) if pagesize == -1 { log.Fatalln("sysconf claims a -1 page size..") } start := ptr &^ uintptr(pagesize-1) // align address to page start ustart := unsafe.Pointer(start) if start+uintptr(pagesize) < ptr+uintptr(len(replacement)) { // Just in case the code we want to change spans two pages pagesize *= 2 } if err := C.mprotect(ustart, C.size_t(pagesize), C.PROT_READ|C.PROT_WRITE|C.PROT_EXEC); err != 0 { log.Fatalln(err) } copy(data, replacement) if err := C.mprotect(ustart, C.size_t(pagesize), C.PROT_READ|C.PROT_EXEC); err != 0 { log.Fatalln(err) } }