Created
July 11, 2022 23:06
-
-
Save racerxdl/d88bb6e9e8f1cd032025f48a43839de7 to your computer and use it in GitHub Desktop.
Golang JIT PoC
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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