Skip to content

Instantly share code, notes, and snippets.

@racerxdl
Created July 11, 2022 23:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save racerxdl/d88bb6e9e8f1cd032025f48a43839de7 to your computer and use it in GitHub Desktop.
Save racerxdl/d88bb6e9e8f1cd032025f48a43839de7 to your computer and use it in GitHub Desktop.
Golang JIT PoC
package main
import (
"github.com/edsrzf/mmap-go"
"reflect"
"unsafe"
)
// Addr returns the address in memory of a byte slice, as a uintptr
func Addr(b []byte) uintptr {
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&b))
return hdr.Data
}
// BuildTo converts a byte-slice into an arbitrary-signatured
// function. The out argument should be a pointer to a variable of
// `func' type.
//
// Arguments to the resulting function will be passed to code using a
// hybrid of the GCC and 6c ABIs: The compiled code will receive, via
// the GCC ABI, a single argument, a void* pointing at the beginning
// of the 6c argument frame. For concreteness, on amd64, a
// func([]byte) int would result in %rdi pointing at the 6c stack
// frame, like so:
//
// 24(%rdi) [ return value ]
// 16(%rdi) [ cap(slice) ]
// 8(%rdi) [ len(slice) ]
// 0(%rdi) [ uint8* data ]
func BuildTo(b []byte, out interface{}) {
buildToInternal(b, out, Build)
}
func buildToInternal(b []byte, out interface{}, build func([]byte) func()) {
v := reflect.ValueOf(out)
if v.Type().Kind() != reflect.Ptr {
panic("BuildTo: must pass a pointer")
}
if v.Elem().Type().Kind() != reflect.Func {
panic("BuildTo: must pass a pointer to func")
}
f := build(b)
ival := *(*struct {
typ uintptr
val uintptr
})(unsafe.Pointer(&out))
// Since we know that out has concrete type of *func(..) ...,
// we know it fits into a word, and thus `val' is just the
// pointer itself (http://research.swtch.com/interfaces)
*(*func())(unsafe.Pointer(ival.val)) = f
}
func Build(b []byte) func() {
dummy := jitcall
fn := &struct {
trampoline uintptr
jitcode uintptr
}{**(**uintptr)(unsafe.Pointer(&dummy)), Addr(b)}
return *(*func())(unsafe.Pointer(&fn))
}
func ToExecutableMemory(data []byte) []byte {
b, _ := mmap.MapRegion(nil, len(data), mmap.EXEC|mmap.RDWR, mmap.ANON, int64(0))
copy(b, data)
return b
}
func jitcall() {}
package main
import (
asm "github.com/twitchyliquid64/golang-asm"
"github.com/twitchyliquid64/golang-asm/obj"
"github.com/twitchyliquid64/golang-asm/obj/x86"
"os"
)
func clearReg(builder *asm.Builder, reg int16) *obj.Prog {
prog := builder.NewProg()
prog.As = x86.AXORQ
prog.From.Type = obj.TYPE_REG
prog.From.Reg = reg
prog.To.Type = obj.TYPE_REG
prog.To.Reg = reg
return prog
}
func movRegToReg(builder *asm.Builder, src, dst int16) *obj.Prog {
prog := builder.NewProg()
prog.As = x86.AMOVQ
prog.From.Type = obj.TYPE_REG
prog.From.Reg = src
prog.To.Type = obj.TYPE_REG
prog.To.Reg = dst
return prog
}
func noop(builder *asm.Builder) *obj.Prog {
prog := builder.NewProg()
prog.As = x86.ANOPL
prog.From.Type = obj.TYPE_REG
prog.From.Reg = x86.REG_AX
return prog
}
func addImmediateByte(builder *asm.Builder, in int32) *obj.Prog {
prog := builder.NewProg()
prog.As = x86.AADDB
prog.To.Type = obj.TYPE_REG
prog.To.Reg = x86.REG_AL
prog.From.Type = obj.TYPE_CONST
prog.From.Offset = int64(in)
return prog
}
func movImmediateByte(builder *asm.Builder, reg int16, in int32) *obj.Prog {
prog := builder.NewProg()
prog.As = x86.AMOVQ
prog.To.Type = obj.TYPE_REG
prog.To.Reg = reg
prog.From.Type = obj.TYPE_CONST
prog.From.Offset = int64(in)
return prog
}
func movMemoryByReg(builder *asm.Builder, reg, memoryReg int16, offset int64) *obj.Prog {
prog := builder.NewProg()
prog.As = x86.AMOVQ
prog.To.Type = obj.TYPE_REG
prog.To.Reg = reg
prog.From.Type = obj.TYPE_MEM
prog.From.Reg = memoryReg
prog.From.Offset = offset
return prog
}
func lea(builder *asm.Builder, reg int16, addr int32) *obj.Prog {
prog := builder.NewProg()
prog.As = x86.ALEAQ
prog.To.Type = obj.TYPE_REG
prog.To.Reg = reg
prog.From.Type = obj.TYPE_MEM
prog.From.Offset = int64(addr)
return prog
}
func syscall(builder *asm.Builder) *obj.Prog {
prog := builder.NewProg()
prog.As = x86.ASYSCALL
prog.To.Type = obj.TYPE_NONE
prog.From.Type = obj.TYPE_NONE
return prog
}
func addRet(builder *asm.Builder) *obj.Prog {
prog := builder.NewProg()
prog.As = obj.ARET
return prog
}
func main() {
huebr := "huebr\n"
b, _ := asm.NewBuilder("amd64", 64)
b.AddInstruction(movRegToReg(b, x86.REG_DI, x86.REG_CX))
b.AddInstruction(movImmediateByte(b, x86.REG_AX, 1))
b.AddInstruction(movImmediateByte(b, x86.REG_DI, int32(os.Stdout.Fd())))
b.AddInstruction(movMemoryByReg(b, x86.REG_SI, x86.REG_CX, 0))
b.AddInstruction(movMemoryByReg(b, x86.REG_DX, x86.REG_CX, 8))
b.AddInstruction(syscall(b))
b.AddInstruction(addRet(b))
insts := ToExecutableMemory(b.Assemble())
var myCall func(data string)
BuildTo(insts, &myCall)
myCall(huebr)
//fmt.Printf("Bin: %x\n", b.Assemble())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment