最終更新 1 month ago

Golang JIT PoC

修正履歴 99e21fb1f5a1d818eabfe3f650acd75795a110a2

jit.go Raw
1package main
2
3import (
4 "github.com/edsrzf/mmap-go"
5 "reflect"
6 "unsafe"
7)
8
9// Addr returns the address in memory of a byte slice, as a uintptr
10func Addr(b []byte) uintptr {
11 hdr := (*reflect.SliceHeader)(unsafe.Pointer(&b))
12 return hdr.Data
13}
14
15// BuildTo converts a byte-slice into an arbitrary-signatured
16// function. The out argument should be a pointer to a variable of
17// `func' type.
18//
19// Arguments to the resulting function will be passed to code using a
20// hybrid of the GCC and 6c ABIs: The compiled code will receive, via
21// the GCC ABI, a single argument, a void* pointing at the beginning
22// of the 6c argument frame. For concreteness, on amd64, a
23// func([]byte) int would result in %rdi pointing at the 6c stack
24// frame, like so:
25//
26// 24(%rdi) [ return value ]
27// 16(%rdi) [ cap(slice) ]
28// 8(%rdi) [ len(slice) ]
29// 0(%rdi) [ uint8* data ]
30func BuildTo(b []byte, out interface{}) {
31 buildToInternal(b, out, Build)
32}
33
34func buildToInternal(b []byte, out interface{}, build func([]byte) func()) {
35 v := reflect.ValueOf(out)
36 if v.Type().Kind() != reflect.Ptr {
37 panic("BuildTo: must pass a pointer")
38 }
39 if v.Elem().Type().Kind() != reflect.Func {
40 panic("BuildTo: must pass a pointer to func")
41 }
42
43 f := build(b)
44
45 ival := *(*struct {
46 typ uintptr
47 val uintptr
48 })(unsafe.Pointer(&out))
49
50 // Since we know that out has concrete type of *func(..) ...,
51 // we know it fits into a word, and thus `val' is just the
52 // pointer itself (http://research.swtch.com/interfaces)
53
54 *(*func())(unsafe.Pointer(ival.val)) = f
55}
56
57func Build(b []byte) func() {
58 dummy := jitcall
59 fn := &struct {
60 trampoline uintptr
61 jitcode uintptr
62 }{**(**uintptr)(unsafe.Pointer(&dummy)), Addr(b)}
63
64 return *(*func())(unsafe.Pointer(&fn))
65}
66
67func ToExecutableMemory(data []byte) []byte {
68 b, _ := mmap.MapRegion(nil, len(data), mmap.EXEC|mmap.RDWR, mmap.ANON, int64(0))
69 copy(b, data)
70 return b
71}
72
73func jitcall() {}
74
main.go Raw
1package main
2
3import (
4 asm "github.com/twitchyliquid64/golang-asm"
5 "github.com/twitchyliquid64/golang-asm/obj"
6 "github.com/twitchyliquid64/golang-asm/obj/x86"
7 "os"
8)
9
10func clearReg(builder *asm.Builder, reg int16) *obj.Prog {
11 prog := builder.NewProg()
12 prog.As = x86.AXORQ
13 prog.From.Type = obj.TYPE_REG
14 prog.From.Reg = reg
15 prog.To.Type = obj.TYPE_REG
16 prog.To.Reg = reg
17 return prog
18}
19
20func movRegToReg(builder *asm.Builder, src, dst int16) *obj.Prog {
21 prog := builder.NewProg()
22
23 prog.As = x86.AMOVQ
24 prog.From.Type = obj.TYPE_REG
25 prog.From.Reg = src
26 prog.To.Type = obj.TYPE_REG
27 prog.To.Reg = dst
28 return prog
29}
30
31func noop(builder *asm.Builder) *obj.Prog {
32 prog := builder.NewProg()
33 prog.As = x86.ANOPL
34 prog.From.Type = obj.TYPE_REG
35 prog.From.Reg = x86.REG_AX
36 return prog
37}
38
39func addImmediateByte(builder *asm.Builder, in int32) *obj.Prog {
40 prog := builder.NewProg()
41 prog.As = x86.AADDB
42 prog.To.Type = obj.TYPE_REG
43 prog.To.Reg = x86.REG_AL
44 prog.From.Type = obj.TYPE_CONST
45 prog.From.Offset = int64(in)
46 return prog
47}
48
49func movImmediateByte(builder *asm.Builder, reg int16, in int32) *obj.Prog {
50 prog := builder.NewProg()
51 prog.As = x86.AMOVQ
52 prog.To.Type = obj.TYPE_REG
53 prog.To.Reg = reg
54 prog.From.Type = obj.TYPE_CONST
55 prog.From.Offset = int64(in)
56 return prog
57}
58
59func movMemoryByReg(builder *asm.Builder, reg, memoryReg int16, offset int64) *obj.Prog {
60 prog := builder.NewProg()
61 prog.As = x86.AMOVQ
62 prog.To.Type = obj.TYPE_REG
63 prog.To.Reg = reg
64 prog.From.Type = obj.TYPE_MEM
65 prog.From.Reg = memoryReg
66 prog.From.Offset = offset
67 return prog
68}
69
70func lea(builder *asm.Builder, reg int16, addr int32) *obj.Prog {
71 prog := builder.NewProg()
72 prog.As = x86.ALEAQ
73 prog.To.Type = obj.TYPE_REG
74 prog.To.Reg = reg
75 prog.From.Type = obj.TYPE_MEM
76 prog.From.Offset = int64(addr)
77
78 return prog
79}
80
81func syscall(builder *asm.Builder) *obj.Prog {
82 prog := builder.NewProg()
83
84 prog.As = x86.ASYSCALL
85 prog.To.Type = obj.TYPE_NONE
86 prog.From.Type = obj.TYPE_NONE
87
88 return prog
89}
90
91func addRet(builder *asm.Builder) *obj.Prog {
92 prog := builder.NewProg()
93
94 prog.As = obj.ARET
95
96 return prog
97}
98func main() {
99 huebr := "huebr\n"
100 b, _ := asm.NewBuilder("amd64", 64)
101
102 b.AddInstruction(movRegToReg(b, x86.REG_DI, x86.REG_CX))
103 b.AddInstruction(movImmediateByte(b, x86.REG_AX, 1))
104 b.AddInstruction(movImmediateByte(b, x86.REG_DI, int32(os.Stdout.Fd())))
105 b.AddInstruction(movMemoryByReg(b, x86.REG_SI, x86.REG_CX, 0))
106 b.AddInstruction(movMemoryByReg(b, x86.REG_DX, x86.REG_CX, 8))
107 b.AddInstruction(syscall(b))
108 b.AddInstruction(addRet(b))
109
110 insts := ToExecutableMemory(b.Assemble())
111
112 var myCall func(data string)
113
114 BuildTo(insts, &myCall)
115
116 myCall(huebr)
117
118 //fmt.Printf("Bin: %x\n", b.Assemble())
119}
120