最終更新 1 month ago

Golang JIT PoC

racerxdl's Avatar Lucas Teske revised this gist 3 years ago. Go to revision

2 files changed, 192 insertions

jit.go(file created)

@@ -0,0 +1,73 @@
1 + package main
2 +
3 + import (
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
10 + func 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 ]
30 + func BuildTo(b []byte, out interface{}) {
31 + buildToInternal(b, out, Build)
32 + }
33 +
34 + func 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 +
57 + func 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 +
67 + func 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 +
73 + func jitcall() {}

main.go(file created)

@@ -0,0 +1,119 @@
1 + package main
2 +
3 + import (
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 +
10 + func 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 +
20 + func 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 +
31 + func 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 +
39 + func 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 +
49 + func 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 +
59 + func 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 +
70 + func 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 +
81 + func 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 +
91 + func addRet(builder *asm.Builder) *obj.Prog {
92 + prog := builder.NewProg()
93 +
94 + prog.As = obj.ARET
95 +
96 + return prog
97 + }
98 + func 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 + }
Newer Older