Skip to content

Files

Latest commit

author
West Damron
Mar 12, 2019
c92fc55 · Mar 12, 2019

History

History

disasm

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
Mar 12, 2019
Mar 12, 2019
Mar 12, 2019
Mar 12, 2019

disasm

Disassemble Go functions at runtime. But why?

See the godocs or otherwise look at the tests for usage examples.

"Basic" Usage

package example

import (
	"fmt"
	"os"

	// importing everything from the package into the current scope makes for less noise
	. "github.com/wdamron/x64"
	"github.com/wdamron/x64/disasm"
	"golang.org/x/arch/x86/x86asm"
	"golang.org/x/sys/unix"
)

const (
	ANON_PRIVATE = unix.MAP_ANON|unix.MAP_PRIVATE

	READ_WRITE = unix.PROT_READ|unix.PROT_WRITE
	READ_EXEC  = unix.PROT_READ|unix.PROT_EXEC
)

func Useless() error {
	mem, err := unix.Mmap(-1, 0, os.Getpagesize(), READ_WRITE, ANON_PRIVATE)
	if err != nil {
		return fmt.Errorf("sys/unix.Mmap failed: %v", err)
	}

	defer unix.Munmap(mem)

	// Assemble a new function (see the x64 package):

	asm := NewAssembler(mem)
	sum := (func(a, b int) int)(nil) // placeholder value

	// Note: the call frame (arguments and returned value) starts at [RSP+8]

	asm.Inst(MOV, RAX, Mem{Base: RSP, Disp: Rel8(8)})   // RAX := a+0(FP)
	asm.Inst(MOV, RBX, Mem{Base: RSP, Disp: Rel8(16)})  // RBX := b+8(FP)
	asm.Inst(ADD, RAX, RBX)                             // RAX += RBX
	asm.Inst(MOV, Mem{Base: RSP, Disp: Rel8(24)}, RAX)  // ret+16(FP) := RAX
	asm.Inst(RET)                                       // return
	if asm.Err() != nil {
		return asm.Err()
	}

	if err := unix.Mprotect(mem, READ_EXEC); err != nil {
		return fmt.Errorf("sys/unix.Mprotect failed: %v", err)
	}

	// Assign the address of the assembled/executable code to the code-pointer within
	// the placeholder function-value:
	if err := SetFunctionCode(&sum, mem); err != nil {
		return err
	}

	if sum(1, 2) != 3 {
		return fmt.Errorf("sum(1, 2) should not equal %v", sum(1, 2))
	}

	// Disassemble the function:

	insts := make([]x86asm.Inst, 0, 5)
	takeWhile := func(inst x86asm.Inst) bool {
		insts = append(insts, inst)
		return inst.Op != x86asm.RET
	}
	if err := disasm.Func(sum, takeWhile); err != nil {
		return err
	}

	for _, inst := range insts {
		fmt.Println(x86asm.IntelSyntax(inst, 0, nil))
	}
	// Outputs:
	//
	// 	mov rax, qword ptr [rsp+0x8]
	// 	mov rbx, qword ptr [rsp+0x10]
	// 	add rax, rbx
	// 	mov qword ptr [rsp+0x18], rax
	// 	ret

	return nil
}