├── go ├── native │ ├── enum │ │ ├── file.go │ │ └── mmap.go │ ├── itimerval.go │ ├── timeval.go │ ├── fdset_darwin.go │ ├── timespec_darwin.go │ ├── timespec_linux32.go │ ├── fdset_linux32.go │ ├── select_linux.go │ ├── timespec_linux64.go │ ├── timespec.go │ ├── fdset_linux64.go │ ├── select_darwin.go │ ├── fdset.go │ ├── statfs_linux.go │ ├── statfs.go │ └── statfs_darwin64.go ├── loader │ ├── type.go │ ├── util.go │ ├── null.go │ ├── load_test.go │ ├── com_test.go │ ├── cgc_test.go │ ├── elf_test.go │ ├── load.go │ ├── macho_test.go │ ├── cgc.go │ ├── com.go │ └── loader.go ├── models │ ├── filter.go │ ├── ins.go │ ├── exit_status.go │ ├── op.go │ ├── symbol.go │ ├── loader.go │ ├── uname.go │ ├── discache.go │ ├── segment.go │ ├── trace │ │ ├── keyframe.go │ │ └── ops_test.go │ ├── task.go │ ├── cpu │ │ ├── cpu.go │ │ ├── pack_uint.go │ │ ├── page_test.go │ │ ├── enums.go │ │ ├── regs.go │ │ └── regs_test.go │ ├── callstack.go │ ├── cli.go │ ├── gate.go │ ├── struc_stream.go │ ├── context.go │ ├── loopdetect_test.go │ ├── async_stream.go │ ├── usercorn.go │ ├── loopdetect.go │ ├── debug │ │ └── debug.go │ └── config.go ├── kernel │ ├── mach │ │ ├── thread.go │ │ ├── misc.go │ │ ├── kernel.go │ │ └── mman.go │ ├── linux │ │ ├── stub.go │ │ ├── unpack │ │ │ ├── mmap_darwin.go │ │ │ ├── mmap_linux_arm.go │ │ │ ├── sockaddr_darwin.go │ │ │ ├── mmap_linux_intel.go │ │ │ ├── mmap.go │ │ │ ├── file.go │ │ │ ├── sockaddr_linux.go │ │ │ └── sockaddr.go │ │ ├── info_darwin.go │ │ ├── mman.go │ │ ├── random.go │ │ ├── pack.go │ │ ├── time.go │ │ ├── signal.go │ │ ├── process.go │ │ ├── eventfd_linux.go │ │ ├── prctl.go │ │ ├── dirent.go │ │ ├── socket.go │ │ ├── info.go │ │ ├── kernel.go │ │ ├── info_linux.go │ │ ├── unpack.go │ │ ├── thread.go │ │ └── elf_auxv.go │ ├── common │ │ ├── errors.go │ │ ├── codec.go │ │ ├── args.go │ │ ├── types.go │ │ ├── syscall.go │ │ ├── trace.go │ │ └── kernel.go │ ├── posix │ │ ├── io_linux.go │ │ ├── error.go │ │ ├── socket_darwin.go │ │ ├── socket_linux.go │ │ ├── sched.go │ │ ├── io_darwin.go │ │ ├── fs.go │ │ ├── iovec.go │ │ ├── stub.go │ │ ├── user.go │ │ ├── process.go │ │ ├── time.go │ │ ├── mman.go │ │ ├── kernel.go │ │ └── stat_linux.go │ ├── darwin │ │ ├── unpack │ │ │ ├── mmap_linux.go │ │ │ ├── mmap_darwin.go │ │ │ ├── sockaddr_darwin.go │ │ │ ├── mmap.go │ │ │ ├── sockaddr_linux.go │ │ │ ├── file.go │ │ │ └── sockaddr.go │ │ ├── unpack.go │ │ ├── io.go │ │ └── kernel.go │ └── redox │ │ ├── kernel.go │ │ └── enum.go ├── arch │ ├── x86_16 │ │ ├── arch_test.go │ │ └── arch.go │ ├── arm │ │ ├── arch_test.go │ │ └── arch.go │ ├── x86 │ │ ├── arch_test.go │ │ ├── darwin.go │ │ └── arch.go │ ├── arm64 │ │ ├── arch_test.go │ │ ├── linux.go │ │ └── arch.go │ ├── mips │ │ ├── arch_test.go │ │ ├── linux.go │ │ └── arch.go │ ├── x86_64 │ │ ├── arch_test.go │ │ ├── abi.go │ │ ├── arch.go │ │ └── linux.go │ ├── sparc │ │ ├── arch_test.go │ │ ├── linux.go │ │ └── arch.go │ ├── m68k │ │ ├── arch_test.go │ │ └── arch.go │ └── arch.go ├── cmd │ ├── run │ │ └── main.go │ ├── main │ │ └── main.go │ ├── cfg │ │ ├── main.go │ │ ├── transaction.go │ │ ├── arch.go │ │ └── cfg.go │ ├── cgc │ │ ├── ids.go │ │ └── io.go │ ├── launcher.go │ ├── rawcmd.go │ ├── shellcode │ │ └── shellcode.go │ ├── com │ │ └── com.go │ ├── trace │ │ ├── ecov.go │ │ └── drcov.go │ ├── imgtrace │ │ └── imgtrace.go │ └── repl │ │ └── repl.go ├── debug │ └── net.go ├── mem.go ├── tests │ ├── rewind_test.go │ └── kernel_test.go ├── cpu │ ├── keystone.go │ ├── capstr.go │ └── unicorn │ │ └── unicorn.go ├── lua │ ├── sugar.go │ ├── bind_cpu.go │ ├── bindings.go │ └── print.go └── ui │ └── repl.go ├── bins ├── x86.linux.cgc ├── x86.linux.elf ├── x86_16.dos.com ├── mipsel.linux.elf ├── test.movsd.hang ├── x86.darwin.macho ├── x86_64.linux.elf └── x86_64.darwin.macho ├── .gitignore ├── .travis.yml ├── LICENSE └── README.md /go/native/enum/file.go: -------------------------------------------------------------------------------- 1 | package enum 2 | 3 | type OpenFlag int 4 | -------------------------------------------------------------------------------- /go/native/enum/mmap.go: -------------------------------------------------------------------------------- 1 | package enum 2 | 3 | type MmapFlag int 4 | type MmapProt int 5 | -------------------------------------------------------------------------------- /bins/x86.linux.cgc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lunixbochs/usercorn/HEAD/bins/x86.linux.cgc -------------------------------------------------------------------------------- /bins/x86.linux.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lunixbochs/usercorn/HEAD/bins/x86.linux.elf -------------------------------------------------------------------------------- /bins/x86_16.dos.com: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lunixbochs/usercorn/HEAD/bins/x86_16.dos.com -------------------------------------------------------------------------------- /bins/mipsel.linux.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lunixbochs/usercorn/HEAD/bins/mipsel.linux.elf -------------------------------------------------------------------------------- /bins/test.movsd.hang: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lunixbochs/usercorn/HEAD/bins/test.movsd.hang -------------------------------------------------------------------------------- /bins/x86.darwin.macho: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lunixbochs/usercorn/HEAD/bins/x86.darwin.macho -------------------------------------------------------------------------------- /bins/x86_64.linux.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lunixbochs/usercorn/HEAD/bins/x86_64.linux.elf -------------------------------------------------------------------------------- /go/loader/type.go: -------------------------------------------------------------------------------- 1 | package loader 2 | 3 | const ( 4 | UNKNOWN = iota 5 | EXEC 6 | DYN 7 | ) 8 | -------------------------------------------------------------------------------- /bins/x86_64.darwin.macho: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lunixbochs/usercorn/HEAD/bins/x86_64.darwin.macho -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | deps/ 2 | 3 | /cgc 4 | /com 5 | /fuzz 6 | /imgtrace 7 | /repl 8 | /shellcode 9 | /usercorn 10 | -------------------------------------------------------------------------------- /go/models/filter.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type Filter interface { 4 | Filter(op Op) []Op 5 | Flush() []Op 6 | } 7 | -------------------------------------------------------------------------------- /go/kernel/mach/thread.go: -------------------------------------------------------------------------------- 1 | package mach 2 | 3 | func (k *MachKernel) Literal__thread_selfid() uint64 { 4 | return 1 5 | } 6 | -------------------------------------------------------------------------------- /go/native/itimerval.go: -------------------------------------------------------------------------------- 1 | package native 2 | 3 | type Itimerval struct { 4 | Interval Timespec 5 | Value Timespec 6 | } 7 | -------------------------------------------------------------------------------- /go/native/timeval.go: -------------------------------------------------------------------------------- 1 | package native 2 | 3 | type Timeval struct { 4 | Sec int64 `struc:"off_t"` 5 | Usec int64 `struc:"off_t"` 6 | } 7 | -------------------------------------------------------------------------------- /go/models/ins.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type Ins interface { 4 | Addr() uint64 5 | Bytes() []byte 6 | Mnemonic() string 7 | OpStr() string 8 | } 9 | -------------------------------------------------------------------------------- /go/arch/x86_16/arch_test.go: -------------------------------------------------------------------------------- 1 | package x86_16 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestX86_16(t *testing.T) { 8 | Arch.SmokeTest(t) 9 | } 10 | -------------------------------------------------------------------------------- /go/native/fdset_darwin.go: -------------------------------------------------------------------------------- 1 | package native 2 | 3 | import ( 4 | "syscall" 5 | ) 6 | 7 | func (f *Fdset32) Native() *syscall.FdSet { 8 | return &syscall.FdSet{Bits: f.Bits} 9 | } 10 | -------------------------------------------------------------------------------- /go/models/exit_status.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import "fmt" 4 | 5 | type ExitStatus int 6 | 7 | func (e ExitStatus) Error() string { 8 | return fmt.Sprintf("exit %d", e) 9 | } 10 | -------------------------------------------------------------------------------- /go/loader/util.go: -------------------------------------------------------------------------------- 1 | package loader 2 | 3 | import ( 4 | "io" 5 | ) 6 | 7 | func getMagic(r io.ReaderAt) []byte { 8 | ret := make([]byte, 4) 9 | r.ReadAt(ret, 0) 10 | return ret 11 | } 12 | -------------------------------------------------------------------------------- /go/kernel/linux/stub.go: -------------------------------------------------------------------------------- 1 | package linux 2 | 3 | func (k *LinuxKernel) Fadvise64() {} 4 | func (k *LinuxKernel) Utimensat() {} 5 | func (k *LinuxKernel) Prlimit() {} 6 | func (k *LinuxKernel) Prlimit64() {} 7 | -------------------------------------------------------------------------------- /go/native/timespec_darwin.go: -------------------------------------------------------------------------------- 1 | package native 2 | 3 | import "syscall" 4 | 5 | func (t *Timespec) Native() *syscall.Timeval { 6 | return &syscall.Timeval{Sec: int64(t.Sec), Usec: int32(t.Nsec / 1000)} 7 | } 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | sudo: true 3 | dist: trusty 4 | cache: ccache 5 | 6 | before_install: 7 | - date 8 | - make deps 9 | - make get 10 | 11 | script: 'make test' 12 | 13 | go: 14 | - 1.5 15 | -------------------------------------------------------------------------------- /go/kernel/mach/misc.go: -------------------------------------------------------------------------------- 1 | package mach 2 | 3 | // verifies a binary signature 4 | func (k *MachKernel) Csops() uint64 { 5 | return 0 6 | } 7 | 8 | func (k *MachKernel) Issetugid() uint64 { 9 | return 0 10 | } 11 | -------------------------------------------------------------------------------- /go/kernel/common/errors.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import "errors" 4 | 5 | var UnknownUnpackType = errors.New("kernel.Unpack() does not support type") 6 | var NoUnpackHandler = errors.New("no kernel.Unpack() handler defined") 7 | -------------------------------------------------------------------------------- /go/models/op.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "encoding/json" 5 | "io" 6 | ) 7 | 8 | type Op interface { 9 | json.Marshaler 10 | 11 | Sizeof() int 12 | Pack([]byte) 13 | Unpack(r io.Reader) (int, error) 14 | } 15 | -------------------------------------------------------------------------------- /go/kernel/posix/io_linux.go: -------------------------------------------------------------------------------- 1 | package posix 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | ) 7 | 8 | func PathFromFd(dirfd int) (string, error) { 9 | p, err := ioutil.ReadFile(fmt.Sprintf("/proc/self/fd/%d", dirfd)) 10 | return string(p), err 11 | } 12 | -------------------------------------------------------------------------------- /go/native/timespec_linux32.go: -------------------------------------------------------------------------------- 1 | // +build 386 arm 2 | // +build linux 3 | 4 | package native 5 | 6 | import "syscall" 7 | 8 | func (t *Timespec) Native() *syscall.Timeval { 9 | return &syscall.Timeval{Sec: int32(t.Sec), Usec: int32(t.Nsec / 1000)} 10 | } 11 | -------------------------------------------------------------------------------- /go/native/fdset_linux32.go: -------------------------------------------------------------------------------- 1 | // +build 386 arm 2 | // +build linux 3 | 4 | package native 5 | 6 | import ( 7 | "syscall" 8 | ) 9 | 10 | // TODO: 32-bit vs 64-bit 11 | func (f *Fdset32) Native() *syscall.FdSet { 12 | return &syscall.FdSet{Bits: f.Bits} 13 | } 14 | -------------------------------------------------------------------------------- /go/native/select_linux.go: -------------------------------------------------------------------------------- 1 | package native 2 | 3 | import ( 4 | "syscall" 5 | ) 6 | 7 | func Select(nfds int, readFds, writeFds *syscall.FdSet, timespec *Timespec) (int, error) { 8 | return syscall.Select(nfds, readFds, writeFds, nil, timespec.Native()) 9 | } 10 | -------------------------------------------------------------------------------- /go/native/timespec_linux64.go: -------------------------------------------------------------------------------- 1 | // +build amd64 arm64 2 | // +build linux 3 | 4 | package native 5 | 6 | import "syscall" 7 | 8 | func (t *Timespec) Native() *syscall.Timeval { 9 | return &syscall.Timeval{Sec: int64(t.Sec), Usec: int64(t.Nsec / 1000)} 10 | } 11 | -------------------------------------------------------------------------------- /go/kernel/posix/error.go: -------------------------------------------------------------------------------- 1 | package posix 2 | 3 | import ( 4 | "syscall" 5 | ) 6 | 7 | const UINT64_MAX = 0xFFFFFFFFFFFFFFFF 8 | 9 | func Errno(err error) uint64 { 10 | if err != nil { 11 | return uint64(int64(-err.(syscall.Errno))) 12 | } 13 | return 0 14 | } 15 | -------------------------------------------------------------------------------- /go/models/symbol.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type Symbol struct { 4 | Name string 5 | Start, End uint64 6 | Dynamic bool 7 | } 8 | 9 | func (s Symbol) Contains(addr uint64) bool { 10 | return s.Start <= addr && (s.Start+s.End > addr || s.End == 0) 11 | } 12 | -------------------------------------------------------------------------------- /go/kernel/mach/kernel.go: -------------------------------------------------------------------------------- 1 | package mach 2 | 3 | import ( 4 | co "github.com/lunixbochs/usercorn/go/kernel/common" 5 | ) 6 | 7 | type MachKernel struct { 8 | *co.KernelBase 9 | } 10 | 11 | func NewKernel() *MachKernel { 12 | return &MachKernel{&co.KernelBase{}} 13 | } 14 | -------------------------------------------------------------------------------- /go/kernel/posix/socket_darwin.go: -------------------------------------------------------------------------------- 1 | package posix 2 | 3 | import ( 4 | "syscall" 5 | ) 6 | 7 | func nativeSelect(nfds int, readfds, writefds, errorfds *syscall.FdSet, timeout *syscall.Timeval) error { 8 | return syscall.Select(nfds, readfds, writefds, errorfds, timeout) 9 | } 10 | -------------------------------------------------------------------------------- /go/cmd/run/main.go: -------------------------------------------------------------------------------- 1 | package run 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/lunixbochs/usercorn/go/cmd" 7 | ) 8 | 9 | func Main(args []string) { 10 | os.Exit(cmd.NewUsercornCmd().Run(args, os.Environ())) 11 | } 12 | 13 | func init() { cmd.Register("run", "execute a binary", Main) } 14 | -------------------------------------------------------------------------------- /go/kernel/posix/socket_linux.go: -------------------------------------------------------------------------------- 1 | package posix 2 | 3 | import ( 4 | "syscall" 5 | ) 6 | 7 | func nativeSelect(nfds int, readfds, writefds, errorfds *syscall.FdSet, timeout *syscall.Timeval) error { 8 | _, err := syscall.Select(nfds, readfds, writefds, errorfds, timeout) 9 | return err 10 | } 11 | -------------------------------------------------------------------------------- /go/native/timespec.go: -------------------------------------------------------------------------------- 1 | package native 2 | 3 | import "time" 4 | 5 | type Timespec struct { 6 | Sec int64 `struc:"off_t"` 7 | Nsec int64 `struc:"off_t"` 8 | } 9 | 10 | func (t *Timespec) Duration() time.Duration { 11 | return time.Duration(t.Sec)*time.Second + time.Duration(t.Nsec)*time.Nanosecond 12 | } 13 | -------------------------------------------------------------------------------- /go/kernel/darwin/unpack/mmap_linux.go: -------------------------------------------------------------------------------- 1 | package unpack 2 | 3 | import ( 4 | "syscall" 5 | ) 6 | 7 | var mmapFlagMap = map[int]int{ 8 | 0x0: syscall.MAP_FILE, 9 | 0x1: syscall.MAP_SHARED, 10 | 0x2: syscall.MAP_PRIVATE, 11 | 12 | 0x10: syscall.MAP_FIXED, 13 | 0x40: syscall.MAP_NORESERVE, 14 | 0x1000: syscall.MAP_ANON, 15 | } 16 | -------------------------------------------------------------------------------- /go/kernel/linux/unpack/mmap_darwin.go: -------------------------------------------------------------------------------- 1 | package unpack 2 | 3 | import ( 4 | "syscall" 5 | ) 6 | 7 | var mmapFlagMap = map[int]int{ 8 | 0x0: syscall.MAP_FILE, 9 | 0x1: syscall.MAP_SHARED, 10 | 0x2: syscall.MAP_PRIVATE, 11 | 12 | 0x10: syscall.MAP_FIXED, 13 | 0x20: syscall.MAP_ANON, 14 | 0x4000: syscall.MAP_NORESERVE, 15 | } 16 | -------------------------------------------------------------------------------- /go/kernel/linux/info_darwin.go: -------------------------------------------------------------------------------- 1 | package linux 2 | 3 | import ( 4 | co "github.com/lunixbochs/usercorn/go/kernel/common" 5 | ) 6 | 7 | func (k *LinuxKernel) Sysinfo(buf co.Obuf) uint64 { 8 | info := Sysinfo_t{ 9 | // FIXME (need sysctl) 10 | } 11 | if err := buf.Pack(&info); err != nil { 12 | return UINT64_MAX // FIXME 13 | } 14 | return 0 15 | } 16 | -------------------------------------------------------------------------------- /go/kernel/linux/mman.go: -------------------------------------------------------------------------------- 1 | package linux 2 | 3 | import ( 4 | co "github.com/lunixbochs/usercorn/go/kernel/common" 5 | "github.com/lunixbochs/usercorn/go/native/enum" 6 | ) 7 | 8 | func (k *LinuxKernel) Mmap2(addrHint, size uint64, prot enum.MmapProt, flags enum.MmapFlag, fd co.Fd, off co.Off) uint64 { 9 | return k.Mmap(addrHint, size, prot, flags, fd, off*0x1000) 10 | } 11 | -------------------------------------------------------------------------------- /go/kernel/posix/sched.go: -------------------------------------------------------------------------------- 1 | package posix 2 | 3 | import "syscall" 4 | 5 | const ( 6 | SCHED_NORMAL = 0 7 | SCHED_FIFO = 1 8 | SCHED_RR = 2 9 | SCHED_BATCH = 3 10 | SCHED_ISO = 4 11 | SCHED_IDLE = 5 12 | SCHED_DEADLINE =6 13 | ) 14 | 15 | func (k *PosixKernel) SchedGetscheduler(pid int) int64 { 16 | if pid < 0 { 17 | return int64(syscall.EINVAL) 18 | } 19 | return SCHED_NORMAL 20 | } 21 | -------------------------------------------------------------------------------- /go/debug/net.go: -------------------------------------------------------------------------------- 1 | package debug 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "os" 7 | ) 8 | 9 | func Accept(host, port string) (net.Conn, error) { 10 | addr := net.JoinHostPort(host, port) 11 | fmt.Fprintf(os.Stderr, "Waiting for connection on %s\n", addr) 12 | ln, err := net.Listen("tcp", addr) 13 | if err != nil { 14 | return nil, err 15 | } 16 | defer ln.Close() 17 | return ln.Accept() 18 | } 19 | -------------------------------------------------------------------------------- /go/kernel/linux/random.go: -------------------------------------------------------------------------------- 1 | package linux 2 | 3 | import ( 4 | "crypto/rand" 5 | co "github.com/lunixbochs/usercorn/go/kernel/common" 6 | ) 7 | 8 | func (k *LinuxKernel) Getrandom(buf co.Obuf, size uint64, flags uint32) uint64 { 9 | tmp := make([]byte, size) 10 | n, _ := rand.Read(tmp) 11 | tmp = tmp[:n] 12 | if err := buf.Pack(tmp); err != nil { 13 | return UINT64_MAX 14 | } 15 | return uint64(n) 16 | } 17 | 18 | -------------------------------------------------------------------------------- /go/arch/arm/arch_test.go: -------------------------------------------------------------------------------- 1 | package arm 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | var testAsm = ` 8 | mov r1, 100 9 | l1: subs r1, 1 10 | bge l1 11 | ` 12 | 13 | func TestArm(t *testing.T) { Arch.SmokeTest(t) } 14 | func TestArmExec(t *testing.T) { Arch.TestExec(t, testAsm) } 15 | func BenchmarkArmRegs(b *testing.B) { Arch.BenchRegs(b) } 16 | func BenchmarkArmExec(b *testing.B) { Arch.BenchExec(b, testAsm) } 17 | -------------------------------------------------------------------------------- /go/native/fdset_linux64.go: -------------------------------------------------------------------------------- 1 | // +build amd64 arm64 2 | // +build linux 3 | 4 | package native 5 | 6 | import ( 7 | "syscall" 8 | ) 9 | 10 | func (f *Fdset32) To64() (out [16]int64) { 11 | for _, fd := range f.Fds() { 12 | out[fd/16] |= (1 << uint(fd) & (32 - 1)) 13 | } 14 | return 15 | } 16 | 17 | // TODO: 32-bit vs 64-bit 18 | func (f *Fdset32) Native() *syscall.FdSet { 19 | return &syscall.FdSet{Bits: f.To64()} 20 | } 21 | -------------------------------------------------------------------------------- /go/arch/x86/arch_test.go: -------------------------------------------------------------------------------- 1 | package x86 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | var testAsm = ` 8 | mov eax, 100 9 | l1: 10 | dec eax 11 | cmp eax, 0 12 | jg l1 13 | ` 14 | 15 | func TestX86(t *testing.T) { Arch.SmokeTest(t) } 16 | func TestX86Exec(t *testing.T) { Arch.TestExec(t, testAsm) } 17 | func BenchmarkX86Regs(b *testing.B) { Arch.BenchRegs(b) } 18 | func BenchmarkX86Exec(b *testing.B) { Arch.BenchExec(b, testAsm) } 19 | -------------------------------------------------------------------------------- /go/kernel/linux/pack.go: -------------------------------------------------------------------------------- 1 | package linux 2 | 3 | import ( 4 | "github.com/lunixbochs/argjoy" 5 | "syscall" 6 | 7 | co "github.com/lunixbochs/usercorn/go/kernel/common" 8 | "github.com/lunixbochs/usercorn/go/native" 9 | ) 10 | 11 | func Pack(buf co.Buf, i interface{}) error { 12 | switch v := i.(type) { 13 | case *syscall.Statfs_t: 14 | return buf.Pack(native.StatfsToLinux(v)) 15 | default: 16 | return argjoy.NoMatch 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /go/loader/null.go: -------------------------------------------------------------------------------- 1 | package loader 2 | 3 | import ( 4 | "encoding/binary" 5 | 6 | "github.com/lunixbochs/usercorn/go/models" 7 | ) 8 | 9 | type NullLoader struct { 10 | LoaderBase 11 | } 12 | 13 | func NewNullLoader(arch, os string, byteOrder binary.ByteOrder, entry uint64) models.Loader { 14 | return &NullLoader{LoaderBase{ 15 | arch: arch, 16 | os: os, 17 | byteOrder: byteOrder, 18 | entry: entry, 19 | }} 20 | } 21 | -------------------------------------------------------------------------------- /go/arch/arm64/arch_test.go: -------------------------------------------------------------------------------- 1 | package arm64 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | var testAsm = ` 8 | mov x1, 100 9 | l1: 10 | subs x1, x1, 1 11 | bge l1 12 | ` 13 | 14 | func TestArm64(t *testing.T) { Arch.SmokeTest(t) } 15 | func TestArm64Exec(t *testing.T) { Arch.TestExec(t, testAsm) } 16 | func BenchmarkArm64Regs(b *testing.B) { Arch.BenchRegs(b) } 17 | func BenchmarkArm64Exec(b *testing.B) { Arch.BenchExec(b, testAsm) } 18 | -------------------------------------------------------------------------------- /go/arch/mips/arch_test.go: -------------------------------------------------------------------------------- 1 | package mips 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | var testAsm = ` 8 | li $t0, 100 9 | l1: 10 | addi $t0, $t0, -1 11 | bgt $t0, 0, l1 12 | ` 13 | 14 | func TestMips(t *testing.T) { Arch.SmokeTest(t) } 15 | func TestMipsExec(t *testing.T) { Arch.TestExec(t, testAsm) } 16 | func BenchmarkMipsRegs(b *testing.B) { Arch.BenchRegs(b) } 17 | func BenchmarkMipsExec(b *testing.B) { Arch.BenchExec(b, testAsm) } 18 | -------------------------------------------------------------------------------- /go/mem.go: -------------------------------------------------------------------------------- 1 | package usercorn 2 | 3 | const ( 4 | BASE = 0x100000 5 | UC_MEM_ALIGN = 0x1000 6 | ) 7 | 8 | func align(addr, size uint64, growl ...bool) (uint64, uint64) { 9 | to := uint64(UC_MEM_ALIGN) 10 | mask := ^(to - 1) 11 | right := addr + size 12 | right = (right + to - 1) & mask 13 | addr &= mask 14 | size = right - addr 15 | if len(growl) > 0 && growl[0] { 16 | size = (size + to - 1) & mask 17 | } 18 | return addr, size 19 | } 20 | -------------------------------------------------------------------------------- /go/arch/x86_64/arch_test.go: -------------------------------------------------------------------------------- 1 | package x86_64 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | var testAsm = ` 8 | mov rax, 100 9 | l1: 10 | dec rax 11 | cmp rax, 0 12 | jg l1 13 | ` 14 | 15 | func TestX86_64(t *testing.T) { Arch.SmokeTest(t) } 16 | func TestX86_64Exec(t *testing.T) { Arch.TestExec(t, testAsm) } 17 | func BenchmarkX86_64Regs(b *testing.B) { Arch.BenchRegs(b) } 18 | func BenchmarkX86_64Exec(b *testing.B) { Arch.BenchExec(b, testAsm) } 19 | -------------------------------------------------------------------------------- /go/models/loader.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "debug/dwarf" 5 | "encoding/binary" 6 | ) 7 | 8 | type Loader interface { 9 | Arch() string 10 | Bits() int 11 | ByteOrder() binary.ByteOrder 12 | OS() string 13 | Entry() uint64 14 | Type() int 15 | Interp() string 16 | Header() (uint64, []byte, int) 17 | Symbols() ([]Symbol, error) 18 | Segments() ([]SegmentData, error) 19 | DataSegment() (uint64, uint64) 20 | DWARF() (*dwarf.Data, error) 21 | } 22 | -------------------------------------------------------------------------------- /go/arch/sparc/arch_test.go: -------------------------------------------------------------------------------- 1 | package sparc 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | var testAsm = ` 8 | set 100, %l0 9 | loop: sub %l0, 1, %l0 10 | cmp %l0, 0 11 | bg loop 12 | nop 13 | ` 14 | 15 | func TestSparc(t *testing.T) { Arch.SmokeTest(t) } 16 | func TestSparcExec(t *testing.T) { Arch.TestExec(t, testAsm) } 17 | func BenchmarkSparcRegs(b *testing.B) { Arch.BenchRegs(b) } 18 | func BenchmarkSparcExec(b *testing.B) { Arch.BenchExec(b, testAsm) } 19 | -------------------------------------------------------------------------------- /go/kernel/linux/time.go: -------------------------------------------------------------------------------- 1 | package linux 2 | 3 | import ( 4 | "time" 5 | 6 | co "github.com/lunixbochs/usercorn/go/kernel/common" 7 | "github.com/lunixbochs/usercorn/go/native" 8 | ) 9 | 10 | func (k *LinuxKernel) Gettimeofday(tp co.Obuf, tz *native.Timespec) uint64 { 11 | now := time.Now() 12 | res := native.Timespec{ 13 | Sec: int64(now.Unix()), 14 | Nsec: int64(now.Nanosecond()), 15 | } 16 | if err := tp.Pack(&res); err != nil { 17 | return UINT64_MAX // FIXME 18 | } 19 | return 0 20 | } 21 | -------------------------------------------------------------------------------- /go/kernel/posix/io_darwin.go: -------------------------------------------------------------------------------- 1 | package posix 2 | 3 | import ( 4 | "bytes" 5 | "syscall" 6 | "unsafe" 7 | ) 8 | 9 | func PathFromFd(dirfd int) (string, error) { 10 | // FIXME? MAXPATHLEN on OS X is currently 1024 11 | buf := make([]byte, 1024) 12 | _, _, errn := syscall.Syscall(syscall.SYS_FCNTL, uintptr(dirfd), uintptr(syscall.F_GETPATH), uintptr(unsafe.Pointer(&buf[0]))) 13 | if errn != 0 { 14 | return "", errn 15 | } 16 | tmp := bytes.SplitN(buf, []byte{0}, 2) 17 | return string(tmp[0]), nil 18 | } 19 | -------------------------------------------------------------------------------- /go/arch/m68k/arch_test.go: -------------------------------------------------------------------------------- 1 | package m68k 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | var testAsm = ` 8 | move.w #100, d1 9 | loop: subq.w #1, d1 10 | cmpq.w #0, d1 11 | bra loop 12 | ` 13 | 14 | func TestM68k(t *testing.T) { Arch.SmokeTest(t) } 15 | func BenchmarkM68kRegs(b *testing.B) { Arch.BenchRegs(b) } 16 | 17 | // keystone can't assemble m68k 18 | // func TestM68kExec(t *testing.T) { Arch.TestExec(t, testAsm) } 19 | // func BenchmarkM68kExec(b *testing.B) { Arch.BenchExec(b, testAsm) } 20 | -------------------------------------------------------------------------------- /go/kernel/darwin/unpack/mmap_darwin.go: -------------------------------------------------------------------------------- 1 | package unpack 2 | 3 | import ( 4 | "syscall" 5 | ) 6 | 7 | var mmapFlagMap = map[int]int{ 8 | 0x0: syscall.MAP_FILE, 9 | 0x1: syscall.MAP_SHARED, 10 | 0x2: syscall.MAP_PRIVATE, 11 | 12 | 0x10: syscall.MAP_FIXED, 13 | 0x20: syscall.MAP_RENAME, 14 | 0x40: syscall.MAP_NORESERVE, 15 | 0x80: syscall.MAP_RESERVED0080, 16 | 0x100: syscall.MAP_NOEXTEND, 17 | 0x200: syscall.MAP_HASSEMAPHORE, 18 | 0x400: syscall.MAP_NOCACHE, 19 | 0x800: syscall.MAP_JIT, 20 | 0x1000: syscall.MAP_ANON, 21 | } 22 | -------------------------------------------------------------------------------- /go/kernel/linux/unpack/mmap_linux_arm.go: -------------------------------------------------------------------------------- 1 | package unpack 2 | 3 | import "syscall" 4 | 5 | var mmapFlagMap = map[int]int{ 6 | 0x0: syscall.MAP_FILE, 7 | 0x1: syscall.MAP_SHARED, 8 | 0x2: syscall.MAP_PRIVATE, 9 | 10 | 0x10: syscall.MAP_FIXED, 11 | 0x20: syscall.MAP_ANON, 12 | 0x4000: syscall.MAP_NORESERVE, 13 | 0x0100: syscall.MAP_GROWSDOWN, 14 | 0x0800: syscall.MAP_DENYWRITE, 15 | 0x1000: syscall.MAP_EXECUTABLE, 16 | 0x2000: syscall.MAP_LOCKED, 17 | 0x8000: syscall.MAP_POPULATE, 18 | 0x10000: syscall.MAP_NONBLOCK, 19 | } 20 | -------------------------------------------------------------------------------- /go/loader/load_test.go: -------------------------------------------------------------------------------- 1 | package loader 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | ) 7 | 8 | func TestLoad(t *testing.T) { 9 | if _, err := LoadFile("../../bins/x86.linux.elf"); err != nil { 10 | t.Fatal(err) 11 | } 12 | if _, err := LoadFile("../../bins/x86.linux.cgc"); err != nil { 13 | t.Fatal(err) 14 | } 15 | if _, err := LoadFile("../../bins/x86.darwin.macho"); err != nil { 16 | t.Fatal(err) 17 | } 18 | if _, err := Load(bytes.NewReader([]byte(""))); err == nil { 19 | t.Fatal("Failed to error on loading bad file.") 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /go/loader/com_test.go: -------------------------------------------------------------------------------- 1 | package loader 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | const comFile = "../../bins/x86_16.dos.com" 8 | 9 | func TestComLoad(t *testing.T) { 10 | _, err := NewComLoader(comFile) 11 | if err != nil { 12 | t.Fatal(err) 13 | } 14 | } 15 | 16 | func TestComSegments(t *testing.T) { 17 | elf, err := NewComLoader(comFile) 18 | if err != nil { 19 | t.Fatal(err) 20 | } 21 | segments, err := elf.Segments() 22 | if err != nil { 23 | t.Fatal(err) 24 | } 25 | if len(segments) == 0 { 26 | t.Fatal("No segments!") 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /go/kernel/linux/signal.go: -------------------------------------------------------------------------------- 1 | package linux 2 | 3 | import ( 4 | "github.com/lunixbochs/usercorn/go/kernel/common" 5 | ) 6 | 7 | const ( 8 | SS_ONSTACK = 1 9 | SS_DISABLE = 2 10 | SS_AUTODISARM = 1 << 31 11 | SS_FLAG_BITS = SS_AUTODISARM 12 | ) 13 | 14 | func (k *LinuxKernel) Sigaltstack(nss common.Buf, oss common.Obuf) uint64 { 15 | //TODO: track the flags properly 16 | if oss.Addr != 0 { 17 | oss.Pack(&k.CurrentStack) 18 | } 19 | if nss.Addr != 0 { 20 | if err := nss.Unpack(&k.CurrentStack); err != nil { 21 | return 1 22 | } 23 | } 24 | return 0 25 | } 26 | -------------------------------------------------------------------------------- /go/kernel/linux/process.go: -------------------------------------------------------------------------------- 1 | package linux 2 | 3 | func (k *LinuxKernel) Geteuid32() int { 4 | return k.PosixKernel.Geteuid() 5 | } 6 | 7 | func (k *LinuxKernel) Getuid32() int { 8 | return k.PosixKernel.Getuid() 9 | } 10 | 11 | func (k *LinuxKernel) Getgid32() int { 12 | return k.PosixKernel.Getgid() 13 | } 14 | 15 | func (k *LinuxKernel) Setgid32(gid int32) int { 16 | return k.PosixKernel.Setgid(int(gid)) 17 | } 18 | 19 | func (k *LinuxKernel) Setuid32(uid int32) int { 20 | return k.PosixKernel.Setuid(int(uid)) 21 | } 22 | 23 | func (k *LinuxKernel) Ugetrlimit() {} 24 | -------------------------------------------------------------------------------- /go/tests/rewind_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/lunixbochs/usercorn/go" 7 | "github.com/lunixbochs/usercorn/go/models" 8 | ) 9 | 10 | func BenchmarkRewind(b *testing.B) { 11 | u, err := usercorn.NewUsercorn("../../bins/x86.linux.elf", nil) 12 | if err != nil { 13 | b.Fatal(err) 14 | } 15 | err = u.Run() 16 | if _, ok := err.(models.ExitStatus); err != nil && !ok { 17 | b.Fatal(err) 18 | } 19 | 20 | for i := 0; i < b.N; i++ { 21 | u.Rewind(1, 0) 22 | pc, _ := u.RegRead(u.Arch().PC) 23 | u.Start(pc, 0) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /go/cmd/main/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/lunixbochs/usercorn/go/cmd" 5 | 6 | _ "github.com/lunixbochs/usercorn/go/cmd/run" 7 | 8 | _ "github.com/lunixbochs/usercorn/go/cmd/cfg" 9 | _ "github.com/lunixbochs/usercorn/go/cmd/cgc" 10 | _ "github.com/lunixbochs/usercorn/go/cmd/com" 11 | _ "github.com/lunixbochs/usercorn/go/cmd/fuzz" 12 | _ "github.com/lunixbochs/usercorn/go/cmd/imgtrace" 13 | _ "github.com/lunixbochs/usercorn/go/cmd/repl" 14 | _ "github.com/lunixbochs/usercorn/go/cmd/shellcode" 15 | _ "github.com/lunixbochs/usercorn/go/cmd/trace" 16 | ) 17 | 18 | func main() { cmd.Main() } 19 | -------------------------------------------------------------------------------- /go/models/uname.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import "strings" 4 | 5 | type Uname struct { 6 | Sysname string 7 | Nodename string 8 | Release string 9 | Version string 10 | Machine string 11 | } 12 | 13 | func pad(s string, length int) string { 14 | if len(s)+1 > length { 15 | s = s[:length-1] 16 | } 17 | return s + strings.Repeat("\x00", length-len(s)) 18 | } 19 | 20 | func (u *Uname) Pad(length int) { 21 | u.Sysname = pad(u.Sysname, length) 22 | u.Nodename = pad(u.Nodename, length) 23 | u.Release = pad(u.Release, length) 24 | u.Version = pad(u.Version, length) 25 | u.Machine = pad(u.Machine, length) 26 | } 27 | -------------------------------------------------------------------------------- /go/kernel/darwin/unpack/sockaddr_darwin.go: -------------------------------------------------------------------------------- 1 | package unpack 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "syscall" 7 | ) 8 | 9 | func sockaddrToNative(a interface{}) syscall.Sockaddr { 10 | switch v := a.(type) { 11 | case *SockaddrUnix: 12 | paths := bytes.SplitN(v.Path[:], []byte{0}, 2) 13 | return &syscall.SockaddrUnix{Name: string(paths[0])} 14 | case *SockaddrInet4: 15 | return &syscall.SockaddrInet4{Port: int(v.Port), Addr: v.Addr} 16 | case *SockaddrInet6: 17 | return &syscall.SockaddrInet6{Port: int(v.Port), Addr: v.Addr} 18 | default: 19 | panic(fmt.Sprintf("sockAddrToNative unsupported type %T", v)) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /go/kernel/linux/unpack/sockaddr_darwin.go: -------------------------------------------------------------------------------- 1 | package unpack 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "syscall" 7 | ) 8 | 9 | func sockaddrToNative(a interface{}) syscall.Sockaddr { 10 | switch v := a.(type) { 11 | case *SockaddrUnix: 12 | paths := bytes.SplitN(v.Path[:], []byte{0}, 2) 13 | return &syscall.SockaddrUnix{Name: string(paths[0])} 14 | case *SockaddrInet4: 15 | return &syscall.SockaddrInet4{Port: int(v.Port), Addr: v.Addr} 16 | case *SockaddrInet6: 17 | return &syscall.SockaddrInet6{Port: int(v.Port), Addr: v.Addr} 18 | default: 19 | panic(fmt.Sprintf("sockAddrToNative unsupported type %T", v)) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /go/native/select_darwin.go: -------------------------------------------------------------------------------- 1 | package native 2 | 3 | import ( 4 | "syscall" 5 | ) 6 | 7 | func Select(nfds int, readFds, writeFds *syscall.FdSet, timespec *Timespec) (int, error) { 8 | timeout := &syscall.Timeval{Sec: int64(timespec.Sec), Usec: int32(timespec.Nsec / 1000)} 9 | if err := syscall.Select(nfds, readFds, writeFds, nil, timeout); err != nil { 10 | return 0, err 11 | } else { 12 | max := 0 13 | read := Fdset32{readFds.Bits} 14 | write := Fdset32{writeFds.Bits} 15 | fds := append(read.Fds(), write.Fds()...) 16 | for _, v := range fds { 17 | if v > max { 18 | max = v 19 | } 20 | } 21 | return max + 1, nil 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /go/kernel/linux/eventfd_linux.go: -------------------------------------------------------------------------------- 1 | package linux 2 | 3 | import ( 4 | "syscall" 5 | ) 6 | 7 | func (k *LinuxKernel) Eventfd2(initval, flags uint) (uint64, error) { 8 | var a3 uintptr 9 | r1, _, errno := syscall.Syscall(syscall.SYS_EVENTFD2, uintptr(initval), uintptr(flags), a3) 10 | if errno != 0 { 11 | return 0, errno 12 | } 13 | return uint64(r1), nil 14 | 15 | } 16 | 17 | func (k *LinuxKernel) Eventfd(initval, flags uint) (uint64, error) { 18 | var a3 uintptr 19 | r1, _, errno := syscall.Syscall(syscall.SYS_EVENTFD2, uintptr(initval), uintptr(flags), a3) 20 | if errno != 0 { 21 | return 0, errno 22 | } 23 | return uint64(r1), nil 24 | 25 | } 26 | -------------------------------------------------------------------------------- /go/kernel/linux/prctl.go: -------------------------------------------------------------------------------- 1 | package linux 2 | 3 | import "fmt" 4 | 5 | // TODO: put these somewhere. ghostrace maybe. 6 | const ( 7 | PR_SET_VMA = 0x53564d41 8 | PR_GET_DUMPABLE = 0x3 9 | PR_SET_DUMPABLE = 0x4 10 | ) 11 | 12 | func (k *LinuxKernel) Prctl(code int, arg uint64) uint64 { 13 | switch code { 14 | case PR_SET_VMA: 15 | //TODO: if there is ever an Android kernel, this is Android only 16 | return 0 17 | case PR_GET_DUMPABLE: 18 | return k.IsDumpable 19 | case PR_SET_DUMPABLE: 20 | if arg == 0 || arg == 1 { 21 | k.IsDumpable = arg 22 | return 0 23 | } else { 24 | return UINT64_MAX 25 | } 26 | } 27 | panic(fmt.Sprintf("unhandled prctl code: 0x%x", code)) 28 | } 29 | -------------------------------------------------------------------------------- /go/kernel/linux/dirent.go: -------------------------------------------------------------------------------- 1 | package linux 2 | 3 | const ( 4 | DT_UNKNOWN = 0 5 | DT_FIFO = 1 6 | DT_CHR = 2 7 | DT_DIR = 4 8 | DT_BLK = 6 9 | DT_REG = 8 10 | DT_LNK = 10 11 | DT_SOCK = 12 12 | DT_WHT = 14 13 | ) 14 | 15 | // TODO: need to differentiate between guests with and without LFS 16 | type Dirent struct { 17 | Ino uint64 `struc:"uint64"` 18 | Off uint64 `struc:"uint64"` 19 | Len int `struc:"uint16"` 20 | Name string 21 | Type int `struc:"uint8"` 22 | } 23 | 24 | type Dirent64 struct { 25 | Ino uint64 `struc:"uint64"` 26 | Off uint64 `struc:"uint64"` 27 | Len int `struc:"uint16"` 28 | Type int `struc:"uint8"` 29 | Name string 30 | } 31 | -------------------------------------------------------------------------------- /go/kernel/linux/unpack/mmap_linux_intel.go: -------------------------------------------------------------------------------- 1 | // +build i386 amd64 2 | // +build linux 3 | 4 | package unpack 5 | 6 | import "syscall" 7 | 8 | var mmapFlagMap = map[int]int{ 9 | 0x0: syscall.MAP_FILE, 10 | 0x1: syscall.MAP_SHARED, 11 | 0x2: syscall.MAP_PRIVATE, 12 | 13 | 0x10: syscall.MAP_FIXED, 14 | 0x20: syscall.MAP_ANON, 15 | 0x40: syscall.MAP_32BIT, 16 | 0x4000: syscall.MAP_NORESERVE, 17 | 0x0100: syscall.MAP_GROWSDOWN, 18 | 0x0800: syscall.MAP_DENYWRITE, 19 | 0x1000: syscall.MAP_EXECUTABLE, 20 | 0x2000: syscall.MAP_LOCKED, 21 | 0x8000: syscall.MAP_POPULATE, 22 | 0x10000: syscall.MAP_NONBLOCK, 23 | 0x20000: syscall.MAP_STACK, 24 | 0x40000: syscall.MAP_HUGETLB, 25 | } 26 | -------------------------------------------------------------------------------- /go/arch/x86_64/abi.go: -------------------------------------------------------------------------------- 1 | package x86_64 2 | 3 | import ( 4 | "github.com/pkg/errors" 5 | uc "github.com/unicorn-engine/unicorn/bindings/go/unicorn" 6 | 7 | "github.com/lunixbochs/usercorn/go/models" 8 | ) 9 | 10 | var AbiRegs = []int{uc.X86_REG_RDI, uc.X86_REG_RSI, uc.X86_REG_RDX, uc.X86_REG_R10, uc.X86_REG_R8, uc.X86_REG_R9} 11 | 12 | func AbiInit(u models.Usercorn, syscall func(models.Usercorn)) error { 13 | _, err := u.HookAdd(uc.HOOK_INSN, func(_ uc.Unicorn) { 14 | syscall(u) 15 | }, 1, 0, uc.X86_INS_SYSCALL) 16 | if err == nil { 17 | _, err = u.HookAdd(uc.HOOK_INSN, func(_ uc.Unicorn) { 18 | syscall(u) 19 | }, 1, 0, uc.X86_INS_SYSENTER) 20 | } 21 | return errors.Wrap(err, "u.HookAdd() failed") 22 | } 23 | -------------------------------------------------------------------------------- /go/native/fdset.go: -------------------------------------------------------------------------------- 1 | package native 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type Fdset32 struct { 8 | Bits [32]int32 9 | } 10 | 11 | func (f *Fdset32) Set(fd int) { 12 | f.Bits[fd/32] |= (1 << (uint(fd) & (32 - 1))) 13 | } 14 | 15 | func (f *Fdset32) Clear(fd int) { 16 | f.Bits[fd/32] &= ^(1 << (uint(fd) & (32 - 1))) 17 | } 18 | 19 | func (f *Fdset32) IsSet(fd int) bool { 20 | return f.Bits[fd/32]&(1<<(uint(fd)&(32-1))) != 0 21 | } 22 | 23 | func (f *Fdset32) Fds() []int { 24 | var out []int 25 | for fd := 0; fd < 1024; fd++ { 26 | if f.IsSet(fd) { 27 | out = append(out, fd) 28 | } 29 | } 30 | return out 31 | } 32 | 33 | func (f *Fdset32) String() string { 34 | return fmt.Sprintf("%v", f.Fds()) 35 | } 36 | -------------------------------------------------------------------------------- /go/cpu/keystone.go: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import ( 4 | ks "github.com/keystone-engine/keystone/bindings/go/keystone" 5 | "github.com/pkg/errors" 6 | ) 7 | 8 | type Keystone struct { 9 | Arch ks.Architecture 10 | Mode ks.Mode 11 | ks *ks.Keystone 12 | } 13 | 14 | func (k *Keystone) Open() (err error) { 15 | k.ks, err = ks.New(k.Arch, k.Mode) 16 | return errors.Wrap(err, "ks.New() failed") 17 | } 18 | 19 | func (k *Keystone) Asm(asm string, addr uint64) ([]byte, error) { 20 | if k.ks == nil { 21 | if err := k.Open(); err != nil { 22 | return nil, err 23 | } 24 | } 25 | out, _, ok := k.ks.Assemble(asm, addr) 26 | if !ok { 27 | return nil, errors.Wrap(k.ks.LastError(), "ks.Assemble() failed") 28 | } 29 | return out, nil 30 | } 31 | -------------------------------------------------------------------------------- /go/kernel/darwin/unpack/mmap.go: -------------------------------------------------------------------------------- 1 | package unpack 2 | 3 | import ( 4 | "github.com/lunixbochs/usercorn/go/models/cpu" 5 | "github.com/lunixbochs/usercorn/go/native/enum" 6 | ) 7 | 8 | var mmapProtMap = map[int]int{ 9 | 0: cpu.PROT_NONE, 10 | 1: cpu.PROT_READ, 11 | 2: cpu.PROT_WRITE, 12 | 4: cpu.PROT_EXEC, 13 | } 14 | 15 | func MmapProt(reg uint64) enum.MmapProt { 16 | var out enum.MmapProt 17 | for a, b := range mmapProtMap { 18 | if int(reg)&a == a { 19 | out |= enum.MmapProt(b) 20 | } 21 | } 22 | return out 23 | } 24 | 25 | func MmapFlag(reg uint64) enum.MmapFlag { 26 | var out enum.MmapFlag 27 | for a, b := range mmapFlagMap { 28 | if int(reg)&a == a { 29 | out |= enum.MmapFlag(b) 30 | } 31 | } 32 | return out 33 | } 34 | -------------------------------------------------------------------------------- /go/kernel/posix/fs.go: -------------------------------------------------------------------------------- 1 | package posix 2 | 3 | import ( 4 | "syscall" 5 | 6 | co "github.com/lunixbochs/usercorn/go/kernel/common" 7 | ) 8 | 9 | func (k *PosixKernel) Statfs(path string, statfs co.Obuf) uint64 { 10 | var tmp syscall.Statfs_t 11 | if err := syscall.Statfs(path, &tmp); err != nil { 12 | return Errno(err) 13 | } 14 | if err := statfs.Pack(&tmp); err != nil { 15 | return UINT64_MAX // FIXME 16 | } 17 | return 0 18 | } 19 | 20 | func (k *PosixKernel) Fstatfs(fd co.Fd, statfs co.Obuf) uint64 { 21 | var tmp syscall.Statfs_t 22 | if err := syscall.Fstatfs(int(fd), &tmp); err != nil { 23 | return Errno(err) 24 | } 25 | if err := statfs.Pack(&tmp); err != nil { 26 | return UINT64_MAX // FIXME 27 | } 28 | return 0 29 | } 30 | -------------------------------------------------------------------------------- /go/kernel/redox/kernel.go: -------------------------------------------------------------------------------- 1 | package redox 2 | 3 | import ( 4 | "github.com/lunixbochs/usercorn/go/kernel/posix" 5 | "github.com/lunixbochs/usercorn/go/models" 6 | ) 7 | 8 | const ( 9 | STACK_BASE = 0x60000000 10 | STACK_SIZE = 0x00800000 11 | ) 12 | 13 | type RedoxKernel struct { 14 | posix.PosixKernel 15 | } 16 | 17 | func NewKernel() *RedoxKernel { 18 | kernel := &RedoxKernel{*posix.NewKernel()} 19 | // FIXME: set up redox packers 20 | // registerUnpack(kernel) 21 | // kernel.Pack = Pack 22 | return kernel 23 | } 24 | 25 | func StackInit(u models.Usercorn, args, env []string) error { 26 | if err := u.MapStack(STACK_BASE, STACK_SIZE, false); err != nil { 27 | return err 28 | } 29 | return posix.StackInit(u, args, env, nil) 30 | } 31 | -------------------------------------------------------------------------------- /go/kernel/linux/socket.go: -------------------------------------------------------------------------------- 1 | package linux 2 | 3 | import ( 4 | "syscall" 5 | 6 | co "github.com/lunixbochs/usercorn/go/kernel/common" 7 | "github.com/lunixbochs/usercorn/go/native" 8 | ) 9 | 10 | func fdcount(bufs ...co.Obuf) int { 11 | count := 0 12 | for _, b := range bufs { 13 | if b.Addr != 0 { 14 | var f native.Fdset32 15 | b.Unpack(&f) 16 | count += len(f.Fds()) 17 | } 18 | } 19 | return count 20 | } 21 | 22 | func (k *LinuxKernel) Select(args []co.Obuf, nfds int, readfds, writefds, errorfds *native.Fdset32, timeout *syscall.Timeval) uint64 { 23 | if errno := k.PosixKernel.Select(args, nfds, readfds, writefds, errorfds, timeout); errno != 0 { 24 | return errno 25 | } 26 | return uint64(fdcount(args[1], args[2], args[3])) 27 | } 28 | -------------------------------------------------------------------------------- /go/kernel/posix/iovec.go: -------------------------------------------------------------------------------- 1 | package posix 2 | 3 | import ( 4 | co "github.com/lunixbochs/usercorn/go/kernel/common" 5 | ) 6 | 7 | type Iovec32 struct { 8 | Base uint32 9 | Len uint32 10 | } 11 | 12 | type Iovec64 struct { 13 | Base uint64 14 | Len uint64 15 | } 16 | 17 | func iovecIter(stream co.Buf, count uint64, bits uint) <-chan Iovec64 { 18 | ret := make(chan Iovec64) 19 | go func() { 20 | st := stream.Struc() 21 | for i := uint64(0); i < count; i++ { 22 | if bits == 64 { 23 | var iovec Iovec64 24 | st.Unpack(&iovec) 25 | ret <- iovec 26 | } else { 27 | var iv32 Iovec32 28 | st.Unpack(&iv32) 29 | ret <- Iovec64{uint64(iv32.Base), uint64(iv32.Len)} 30 | } 31 | } 32 | close(ret) 33 | }() 34 | return ret 35 | } 36 | -------------------------------------------------------------------------------- /go/loader/cgc_test.go: -------------------------------------------------------------------------------- 1 | package loader 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "io/ioutil" 7 | "testing" 8 | ) 9 | 10 | var cgcFile io.ReaderAt 11 | 12 | func init() { 13 | p, err := ioutil.ReadFile("../../bins/x86.linux.cgc") 14 | if err != nil { 15 | panic(err) 16 | } 17 | cgcFile = bytes.NewReader(p) 18 | } 19 | 20 | func TestCgcLoad(t *testing.T) { 21 | _, err := NewCgcLoader(cgcFile, "any") 22 | if err != nil { 23 | t.Fatal(err) 24 | } 25 | } 26 | 27 | func TestCgcSegments(t *testing.T) { 28 | elf, err := NewCgcLoader(cgcFile, "any") 29 | if err != nil { 30 | t.Fatal(err) 31 | } 32 | segments, err := elf.Segments() 33 | if err != nil { 34 | t.Fatal(err) 35 | } 36 | if len(segments) == 0 { 37 | t.Fatal("No segments found.") 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /go/kernel/posix/stub.go: -------------------------------------------------------------------------------- 1 | package posix 2 | 3 | import ( 4 | co "github.com/lunixbochs/usercorn/go/kernel/common" 5 | ) 6 | 7 | func (k *PosixKernel) Ioctl(fd co.Fd, req uint64) {} 8 | func (k *PosixKernel) Fcntl(fd co.Fd, cmd int) {} 9 | func (k *PosixKernel) Fcntl64(fd co.Fd, cmd int) {} 10 | 11 | func (k *PosixKernel) RtSigprocmask() {} 12 | func (k *PosixKernel) RtSigaction() {} 13 | func (k *PosixKernel) SchedYield() {} 14 | func (k *PosixKernel) Madvise() {} 15 | func (k *PosixKernel) Mlock() {} 16 | func (k *PosixKernel) Munlock() {} 17 | func (k *PosixKernel) Mlockall() {} 18 | func (k *PosixKernel) Munlockall() {} 19 | 20 | func (k *PosixKernel) Swapon() {} 21 | func (k *PosixKernel) Swapoff() {} 22 | 23 | func (k *PosixKernel) Gettid() int { return 0 } 24 | -------------------------------------------------------------------------------- /go/lua/sugar.go: -------------------------------------------------------------------------------- 1 | package lua 2 | 3 | var sugarRc = ` 4 | getmetatable("").__mod = func(a, b) 5 | if type(b) == 'table' then 6 | return string.format(a, unpack(b)) 7 | end 8 | return string.format(a, b) 9 | end 10 | 11 | func hex(s) return '%x' % s end 12 | func ord(s) return string.byte(s, 1) end 13 | func chr(s) return string.char(s) end 14 | 15 | func range(a, b, c) 16 | local i, stop, step = 0, a, 1 17 | if b != nil then 18 | if c != nil then step = c end 19 | i, stop = a, b 20 | end 21 | i = i - 1 22 | return func() 23 | i = i + step 24 | if (step > 0 and i < stop) or (step < 0 and i > stop) then 25 | return i 26 | end 27 | end 28 | end 29 | 30 | func hexdump(s) 31 | print '%x' % s 32 | end 33 | ` 34 | -------------------------------------------------------------------------------- /go/cmd/cfg/main.go: -------------------------------------------------------------------------------- 1 | package cfg 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/lunixbochs/usercorn/go/cmd" 7 | ) 8 | 9 | func Main(args []string) { 10 | var backtrack, json *bool 11 | c := cmd.NewUsercornCmd() 12 | 13 | c.SetupFlags = func() error { 14 | backtrack = c.Flags.Bool("backtrack", false, "recursively backtrack cfg emulator") 15 | json = c.Flags.Bool("json", false, "use json cfg output") 16 | return nil 17 | } 18 | c.RunUsercorn = func() error { 19 | c.Usercorn.Gate().Lock() 20 | go func() { 21 | err := c.Usercorn.Run() 22 | if err != nil { 23 | panic(err) 24 | } 25 | }() 26 | return CfgMain(c.Usercorn, *backtrack, *json) 27 | } 28 | os.Exit(c.Run(args, os.Environ())) 29 | } 30 | 31 | func init() { cmd.Register("cfg", "explore a program's control flow graph", Main) } 32 | -------------------------------------------------------------------------------- /go/kernel/common/codec.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "github.com/lunixbochs/argjoy" 5 | "github.com/pkg/errors" 6 | ) 7 | 8 | func (k *KernelBase) commonArgCodec(arg interface{}, vals []interface{}) error { 9 | if reg, ok := vals[0].(uint64); ok { 10 | switch v := arg.(type) { 11 | case *Buf: 12 | *v = NewBuf(k, reg) 13 | case *Obuf: 14 | *v = Obuf{NewBuf(k, reg)} 15 | case *Len: 16 | *v = Len(reg) 17 | case *Off: 18 | *v = Off(reg) 19 | case *Fd: 20 | *v = Fd(reg) 21 | case *Ptr: 22 | *v = Ptr(reg) 23 | case *string: 24 | s, err := k.U.Mem().ReadStrAt(reg) 25 | if err != nil { 26 | return errors.Wrapf(err, "ReadStrAt(%#x) failed", reg) 27 | } 28 | *v = s 29 | default: 30 | return argjoy.NoMatch 31 | } 32 | return nil 33 | } 34 | return argjoy.NoMatch 35 | } 36 | -------------------------------------------------------------------------------- /go/kernel/darwin/unpack/sockaddr_linux.go: -------------------------------------------------------------------------------- 1 | package unpack 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "syscall" 7 | ) 8 | 9 | func sockaddrToNative(a interface{}) syscall.Sockaddr { 10 | switch v := a.(type) { 11 | case *SockaddrUnix: 12 | paths := bytes.SplitN(v.Path[:], []byte{0}, 2) 13 | return &syscall.SockaddrUnix{Name: string(paths[0])} 14 | case *SockaddrInet4: 15 | return &syscall.SockaddrInet4{Port: int(v.Port), Addr: v.Addr} 16 | case *SockaddrInet6: 17 | return &syscall.SockaddrInet6{Port: int(v.Port), Addr: v.Addr} 18 | case SockaddrLinklayer: 19 | return &syscall.SockaddrLinklayer{ 20 | Protocol: v.Protocol, Ifindex: int(v.Ifindex), Hatype: v.Hatype, 21 | Pkttype: v.Pkttype, Halen: v.Halen, 22 | } 23 | default: 24 | panic(fmt.Sprintf("sockAddrToNative unsupported type %T", v)) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /go/tests/kernel_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "encoding/binary" 5 | "testing" 6 | 7 | "github.com/lunixbochs/usercorn/go" 8 | co "github.com/lunixbochs/usercorn/go/kernel/common" 9 | "github.com/lunixbochs/usercorn/go/loader" 10 | ) 11 | 12 | type PosixKernel struct { 13 | co.KernelBase 14 | exitCode int 15 | } 16 | 17 | func (k *PosixKernel) Exit(code int) uint64 { 18 | k.exitCode = code 19 | return 44 20 | } 21 | 22 | func TestKernel(t *testing.T) { 23 | l := loader.NewNullLoader("x86", "linux", binary.LittleEndian, 0) 24 | u, _ := usercorn.NewUsercornRaw(l, nil) 25 | 26 | kernel := &PosixKernel{} 27 | ret := co.Lookup(u, kernel, "exit").Call([]uint64{43}) 28 | if kernel.exitCode != 43 { 29 | t.Fatal("Syscall failed.") 30 | } 31 | if ret != 44 { 32 | t.Fatal("Syscall return failed.") 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /go/models/discache.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "bytes" 5 | "sync" 6 | ) 7 | 8 | type DiscacheEntry struct { 9 | Addr uint64 10 | Mem []byte 11 | Dis []Ins 12 | } 13 | 14 | type Discache struct { 15 | sync.RWMutex 16 | cache map[uint64]*DiscacheEntry 17 | } 18 | 19 | func NewDiscache() *Discache { 20 | return &Discache{cache: make(map[uint64]*DiscacheEntry)} 21 | } 22 | 23 | func (d *Discache) Get(addr uint64, mem []byte) *DiscacheEntry { 24 | d.RLock() 25 | if ent, ok := d.cache[addr]; ok { 26 | if bytes.Equal(mem, ent.Mem) { 27 | d.RUnlock() 28 | return ent 29 | } 30 | } 31 | d.RUnlock() 32 | return nil 33 | } 34 | 35 | func (d *Discache) Put(addr uint64, mem []byte, dis []Ins) { 36 | d.Lock() 37 | d.cache[addr] = &DiscacheEntry{ 38 | Addr: addr, 39 | Mem: mem, 40 | Dis: dis, 41 | } 42 | d.Unlock() 43 | } 44 | -------------------------------------------------------------------------------- /go/kernel/linux/unpack/mmap.go: -------------------------------------------------------------------------------- 1 | package unpack 2 | 3 | import ( 4 | uc "github.com/unicorn-engine/unicorn/bindings/go/unicorn" 5 | 6 | "github.com/lunixbochs/usercorn/go/native/enum" 7 | ) 8 | 9 | var mmapProtMap = map[int]int{ 10 | 0: uc.PROT_NONE, 11 | 1: uc.PROT_READ, 12 | 2: uc.PROT_WRITE, 13 | 4: uc.PROT_EXEC, 14 | // FIXME? 15 | // 0x01000000: syscall.PROT_GROWSDOWN, 16 | // 0x02000000: syscall.PROT_GROWSUP, 17 | } 18 | 19 | func MmapProt(reg uint64) enum.MmapProt { 20 | var out enum.MmapProt 21 | for a, b := range mmapProtMap { 22 | if int(reg)&a == a { 23 | out |= enum.MmapProt(b) 24 | } 25 | } 26 | return out 27 | } 28 | 29 | func MmapFlag(reg uint64) enum.MmapFlag { 30 | var out enum.MmapFlag 31 | for a, b := range mmapFlagMap { 32 | if int(reg)&a == a { 33 | out |= enum.MmapFlag(b) 34 | } 35 | } 36 | return out 37 | } 38 | -------------------------------------------------------------------------------- /go/kernel/linux/unpack/file.go: -------------------------------------------------------------------------------- 1 | package unpack 2 | 3 | import ( 4 | "syscall" 5 | 6 | "github.com/lunixbochs/usercorn/go/native/enum" 7 | ) 8 | 9 | var fileModeMap = map[int]int{ 10 | 00: syscall.O_RDONLY, 11 | 01: syscall.O_WRONLY, 12 | 02: syscall.O_RDWR, 13 | 0100: syscall.O_CREAT, 14 | 0200: syscall.O_EXCL, 15 | 0400: syscall.O_NOCTTY, 16 | 01000: syscall.O_TRUNC, 17 | 02000: syscall.O_APPEND, 18 | 04000: syscall.O_NONBLOCK, 19 | 010000: syscall.O_DSYNC, 20 | 04010000: syscall.O_SYNC, 21 | 0200000: syscall.O_DIRECTORY, 22 | 0400000: syscall.O_NOFOLLOW, 23 | 02000000: syscall.O_CLOEXEC, 24 | } 25 | 26 | func OpenFlag(reg uint64) enum.OpenFlag { 27 | var out enum.OpenFlag 28 | for a, b := range fileModeMap { 29 | if int(reg)&a == a { 30 | out |= enum.OpenFlag(b) 31 | } 32 | } 33 | return out 34 | } 35 | -------------------------------------------------------------------------------- /go/models/segment.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type SegmentData struct { 4 | Off uint64 5 | Addr, Size uint64 6 | Prot int 7 | DataFunc func() ([]byte, error) 8 | } 9 | 10 | func (s *SegmentData) Data() ([]byte, error) { 11 | return s.DataFunc() 12 | } 13 | 14 | func (s *SegmentData) ContainsPhys(addr uint64) bool { 15 | return s.Off <= addr && addr < s.Off+s.Size 16 | } 17 | 18 | func (s *SegmentData) ContainsVirt(addr uint64) bool { 19 | return s.Addr <= addr && addr < s.Addr+s.Size 20 | } 21 | 22 | type Segment struct { 23 | Start, End uint64 24 | Prot int 25 | } 26 | 27 | func (s *Segment) Overlaps(o *Segment) bool { 28 | return (s.Start >= o.Start && s.Start < o.End) || (o.Start >= s.Start && o.Start < s.End) 29 | } 30 | 31 | func (s *Segment) Merge(o *Segment) { 32 | if s.Start > o.Start { 33 | s.Start = o.Start 34 | } 35 | if s.End < o.End { 36 | s.End = o.End 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /go/kernel/linux/unpack/sockaddr_linux.go: -------------------------------------------------------------------------------- 1 | package unpack 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "syscall" 7 | ) 8 | 9 | func sockaddrToNative(a interface{}) syscall.Sockaddr { 10 | switch v := a.(type) { 11 | case *SockaddrUnix: 12 | paths := bytes.SplitN(v.Path[:], []byte{0}, 2) 13 | return &syscall.SockaddrUnix{Name: string(paths[0])} 14 | case *SockaddrInet4: 15 | return &syscall.SockaddrInet4{Port: int(v.Port), Addr: v.Addr} 16 | case *SockaddrInet6: 17 | return &syscall.SockaddrInet6{Port: int(v.Port), Addr: v.Addr} 18 | case *SockaddrLinklayer: 19 | return &syscall.SockaddrLinklayer{ 20 | Protocol: v.Protocol, Ifindex: int(v.Ifindex), Hatype: v.Hatype, 21 | Pkttype: v.Pkttype, Halen: v.Halen, 22 | } 23 | case *SockaddrNetlink: 24 | return &syscall.SockaddrNetlink{Pad: v.Pad, Pid: v.Pid, Groups: v.Groups} 25 | default: 26 | panic(fmt.Sprintf("sockAddrToNative unsupported type %T", v)) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /go/kernel/linux/info.go: -------------------------------------------------------------------------------- 1 | package linux 2 | 3 | import ( 4 | "github.com/lunixbochs/struc" 5 | 6 | co "github.com/lunixbochs/usercorn/go/kernel/common" 7 | "github.com/lunixbochs/usercorn/go/models" 8 | ) 9 | 10 | type Sysinfo_t struct { 11 | Uptime struc.Off_t 12 | Loads [3]struc.Size_t 13 | Totalram struc.Size_t 14 | Freeram struc.Size_t 15 | Sharedram struc.Size_t 16 | Bufferram struc.Size_t 17 | Totalswap struc.Size_t 18 | Freeswap struc.Size_t 19 | Procs uint16 20 | Totalhigh struc.Size_t 21 | Freehigh struc.Size_t 22 | Unit uint32 23 | } 24 | 25 | func (k *LinuxKernel) Uname(buf co.Buf) { 26 | uname := &models.Uname{ 27 | Sysname: "Linux", 28 | Nodename: "usercorn", 29 | Release: "3.13.0-24-generic", 30 | Version: "normal copy of Linux minding my business", 31 | Machine: k.U.Loader().Arch(), 32 | } 33 | // Pad is both OS and arch dependent? :( 34 | uname.Pad(65) 35 | buf.Pack(uname) 36 | } 37 | -------------------------------------------------------------------------------- /go/models/trace/keyframe.go: -------------------------------------------------------------------------------- 1 | package trace 2 | 3 | import ( 4 | "github.com/lunixbochs/usercorn/go/models" 5 | ) 6 | 7 | type keyframe struct { 8 | regEnums []int 9 | regs []uint64 10 | spregs map[int][]byte 11 | writes map[uint64][]byte 12 | 13 | // this is both maps and unmaps to ensure they stay in order 14 | maps []models.Op 15 | } 16 | 17 | func (k *keyframe) reset() { 18 | k.regs = make([]uint64, len(k.regEnums)) 19 | k.spregs = make(map[int][]byte) 20 | k.writes = make(map[uint64][]byte) 21 | k.maps = nil 22 | } 23 | 24 | func (k *keyframe) op() *OpKeyframe { 25 | frame := &OpKeyframe{} 26 | frame.Ops = k.maps 27 | for i, reg := range k.regs { 28 | op := &OpReg{Num: uint16(k.regEnums[i]), Val: reg} 29 | frame.Ops = append(frame.Ops, op) 30 | } 31 | // TODO handle spregs 32 | for addr, data := range k.writes { 33 | op := &OpMemWrite{Addr: addr, Data: data} 34 | frame.Ops = append(frame.Ops, op) 35 | } 36 | return frame 37 | } 38 | -------------------------------------------------------------------------------- /go/kernel/darwin/unpack/file.go: -------------------------------------------------------------------------------- 1 | package unpack 2 | 3 | import ( 4 | "syscall" 5 | 6 | "github.com/lunixbochs/usercorn/go/native/enum" 7 | ) 8 | 9 | var fileModeMap = map[int]int{ 10 | 0x0: syscall.O_RDONLY, 11 | 0x1: syscall.O_WRONLY, 12 | 0x2: syscall.O_RDWR, 13 | 0x4: syscall.O_NONBLOCK, 14 | 0x8: syscall.O_APPEND, 15 | 0x80: syscall.O_SYNC, 16 | 0x100: syscall.O_NOFOLLOW, 17 | 0x400: syscall.O_TRUNC, 18 | 0x200: syscall.O_CREAT, 19 | 0x800: syscall.O_EXCL, 20 | 0x20000: syscall.O_NOCTTY, 21 | 0x100000: syscall.O_DIRECTORY, 22 | 0x400000: syscall.O_DSYNC, 23 | 0x1000000: syscall.O_CLOEXEC, 24 | 25 | // darwin-only? 26 | // 0x8000: O_EVTONLY, 27 | // 0x200000: O_SYMLINK, 28 | } 29 | 30 | func OpenFlag(reg uint64) enum.OpenFlag { 31 | var out enum.OpenFlag 32 | for a, b := range fileModeMap { 33 | if int(reg)&a == a { 34 | out |= enum.OpenFlag(b) 35 | } 36 | } 37 | return out 38 | } 39 | -------------------------------------------------------------------------------- /go/models/task.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "encoding/binary" 5 | 6 | "github.com/lunixbochs/usercorn/go/models/cpu" 7 | ) 8 | 9 | type Task interface { 10 | cpu.Cpu 11 | 12 | // Cpu wrappers 13 | Mappings() cpu.Pages 14 | // deprecated: only used by RunAsm 15 | MemReserve(addr, size uint64, force bool) (*cpu.Page, error) 16 | Mmap(addr, size uint64, prot int, fixed bool, desc string, file *cpu.FileDesc) (uint64, error) 17 | Malloc(size uint64, desc string) (uint64, error) 18 | 19 | PackAddr(buf []byte, n uint64) ([]byte, error) 20 | UnpackAddr(buf []byte) uint64 21 | PopBytes(p []byte) error 22 | PushBytes(p []byte) (uint64, error) 23 | Pop() (uint64, error) 24 | Push(n uint64) (uint64, error) 25 | RegDump() ([]RegVal, error) 26 | 27 | // Helpers 28 | Arch() *Arch 29 | OS() string 30 | Bits() uint 31 | ByteOrder() binary.ByteOrder 32 | Asm(asm string, addr uint64) ([]byte, error) 33 | Dis(addr, size uint64, showBytes bool) (string, error) 34 | } 35 | -------------------------------------------------------------------------------- /go/loader/elf_test.go: -------------------------------------------------------------------------------- 1 | package loader 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "io/ioutil" 7 | "testing" 8 | ) 9 | 10 | var elfFile io.ReaderAt 11 | 12 | func init() { 13 | p, err := ioutil.ReadFile("../../bins/x86.linux.elf") 14 | if err != nil { 15 | panic(err) 16 | } 17 | elfFile = bytes.NewReader(p) 18 | } 19 | 20 | func TestElfLoad(t *testing.T) { 21 | _, err := NewElfLoader(elfFile, "any") 22 | if err != nil { 23 | t.Fatal(err) 24 | } 25 | } 26 | 27 | func TestElfSymbol(t *testing.T) { 28 | elf, err := NewElfLoader(elfFile, "any") 29 | if err != nil { 30 | t.Fatal(err) 31 | } 32 | _, err = elf.Symbols() 33 | if err != nil { 34 | t.Fatal(err) 35 | } 36 | } 37 | 38 | func TestElfSegments(t *testing.T) { 39 | elf, err := NewElfLoader(elfFile, "any") 40 | if err != nil { 41 | t.Fatal(err) 42 | } 43 | segments, err := elf.Segments() 44 | if err != nil { 45 | t.Fatal(err) 46 | } 47 | if len(segments) == 0 { 48 | t.Fatal("No segments found.") 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /go/cmd/cgc/ids.go: -------------------------------------------------------------------------------- 1 | package cgc 2 | 3 | import ( 4 | "os" 5 | "os/exec" 6 | "syscall" 7 | ) 8 | 9 | func newFile(fd int) *os.File { 10 | return os.NewFile(uintptr(fd), "") 11 | } 12 | 13 | func NewIDS(rules string, debug bool) (*os.File, *os.File, error) { 14 | pair1, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM, 0) 15 | if err != nil { 16 | return nil, nil, err 17 | } 18 | pair2, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM, 0) 19 | if err != nil { 20 | return nil, nil, err 21 | } 22 | 23 | pov2cb := newFile(pair1[0]) 24 | cb2pov := newFile(pair2[0]) 25 | 26 | args := []string{"--rules", rules} 27 | if debug { 28 | args = append(args, "--debug") 29 | } 30 | cmd := exec.Command("cb-proxy-samurai", args...) 31 | cmd.ExtraFiles = []*os.File{newFile(pair1[1]), newFile(pair2[1])} 32 | cmd.Stdout = os.Stdout 33 | cmd.Stderr = os.Stderr 34 | if err := cmd.Start(); err != nil { 35 | return nil, nil, err 36 | } 37 | return pov2cb, cb2pov, nil 38 | } 39 | -------------------------------------------------------------------------------- /go/kernel/linux/kernel.go: -------------------------------------------------------------------------------- 1 | package linux 2 | 3 | import ( 4 | "github.com/lunixbochs/usercorn/go/kernel/posix" 5 | "github.com/lunixbochs/usercorn/go/models" 6 | ) 7 | 8 | const ( 9 | STACK_BASE = 0xbf800000 10 | STACK_SIZE = 0x00800000 11 | ) 12 | 13 | 14 | type sigaltstack_t struct { 15 | Stackpointer uint64 16 | Size int64 17 | Flags int64 18 | } 19 | 20 | type LinuxKernel struct { 21 | posix.PosixKernel 22 | CurrentStack sigaltstack_t 23 | IsDumpable uint64 24 | } 25 | 26 | func NewKernel() *LinuxKernel { 27 | kernel := &LinuxKernel{ 28 | *posix.NewKernel(), 29 | sigaltstack_t{}, 30 | 1, 31 | } 32 | registerUnpack(kernel) 33 | kernel.Pack = Pack 34 | return kernel 35 | } 36 | 37 | func StackInit(u models.Usercorn, args, env []string) error { 38 | if err := u.MapStack(STACK_BASE, STACK_SIZE, false); err != nil { 39 | return err 40 | } 41 | auxv, err := SetupElfAuxv(u) 42 | if err != nil { 43 | return err 44 | } 45 | return posix.StackInit(u, args, env, auxv) 46 | } 47 | -------------------------------------------------------------------------------- /go/loader/load.go: -------------------------------------------------------------------------------- 1 | package loader 2 | 3 | import ( 4 | "bytes" 5 | "github.com/pkg/errors" 6 | "io" 7 | "io/ioutil" 8 | 9 | "github.com/lunixbochs/usercorn/go/models" 10 | ) 11 | 12 | var UnknownMagic = errors.New("Could not identify file magic.") 13 | 14 | func LoadFile(path string) (models.Loader, error) { 15 | return LoadFileArch(path, "any") 16 | } 17 | 18 | func Load(r io.ReaderAt) (models.Loader, error) { 19 | return LoadArch(r, "any") 20 | } 21 | 22 | func LoadFileArch(path string, arch string) (models.Loader, error) { 23 | p, err := ioutil.ReadFile(path) 24 | if err != nil { 25 | return nil, err 26 | } 27 | return LoadArch(bytes.NewReader(p), arch) 28 | } 29 | 30 | func LoadArch(r io.ReaderAt, arch string) (models.Loader, error) { 31 | if MatchElf(r) { 32 | return NewElfLoader(r, arch) 33 | } else if MatchMachO(r) { 34 | return NewMachOLoader(r, arch) 35 | } else if MatchCgc(r) { 36 | return NewCgcLoader(r, arch) 37 | } else { 38 | return nil, errors.WithStack(UnknownMagic) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /go/loader/macho_test.go: -------------------------------------------------------------------------------- 1 | package loader 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "io/ioutil" 7 | "testing" 8 | ) 9 | 10 | var machoFile io.ReaderAt 11 | 12 | func init() { 13 | p, err := ioutil.ReadFile("../../bins/x86.darwin.macho") 14 | if err != nil { 15 | panic(err) 16 | } 17 | machoFile = bytes.NewReader(p) 18 | } 19 | 20 | func TestMachOLoad(t *testing.T) { 21 | _, err := NewMachOLoader(machoFile, "any") 22 | if err != nil { 23 | t.Fatal(err) 24 | } 25 | } 26 | 27 | func TestMachOSymbol(t *testing.T) { 28 | macho, err := NewMachOLoader(machoFile, "any") 29 | if err != nil { 30 | t.Fatal(err) 31 | } 32 | _, err = macho.Symbols() 33 | if err != nil { 34 | t.Fatal(err) 35 | } 36 | } 37 | 38 | func TestMachOSegments(t *testing.T) { 39 | macho, err := NewMachOLoader(machoFile, "any") 40 | if err != nil { 41 | t.Fatal(err) 42 | } 43 | segments, err := macho.Segments() 44 | if err != nil { 45 | t.Fatal(err) 46 | } 47 | if len(segments) == 0 { 48 | t.Fatal("No segments found.") 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /go/models/cpu/cpu.go: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | // This interface abstracts the minimum functionality Usercorn requires in a CPU emulator. 4 | type Cpu interface { 5 | // memory mapping 6 | MemMap(addr, size uint64, prot int) error 7 | MemProt(addr, size uint64, prot int) error 8 | MemUnmap(addr, size uint64) error 9 | 10 | // memory IO 11 | MemRead(addr, size uint64) ([]byte, error) 12 | MemReadInto(p []byte, addr uint64) error 13 | MemWrite(addr uint64, p []byte) error 14 | 15 | // register IO 16 | RegRead(reg int) (uint64, error) 17 | RegWrite(reg int, val uint64) error 18 | 19 | // execution 20 | Start(begin, until uint64) error 21 | Stop() error 22 | 23 | // hooks 24 | HookAdd(htype int, cb interface{}, begin, end uint64, extra ...int) (Hook, error) 25 | HookDel(hook Hook) error 26 | 27 | // save/restore entire CPU state 28 | ContextSave(reuse interface{}) (interface{}, error) 29 | ContextRestore(ctx interface{}) error 30 | 31 | // cleanup 32 | Close() error 33 | 34 | // leaky abstraction 35 | Backend() interface{} 36 | } 37 | -------------------------------------------------------------------------------- /go/loader/cgc.go: -------------------------------------------------------------------------------- 1 | package loader 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | 7 | "github.com/lunixbochs/usercorn/go/models" 8 | ) 9 | 10 | var cgcMagic = []byte{0x7f, 0x43, 0x47, 0x43} 11 | var elfMagicReader = bytes.NewReader(elfMagic) 12 | 13 | func MatchCgc(r io.ReaderAt) bool { 14 | return bytes.Equal(getMagic(r), cgcMagic) 15 | } 16 | 17 | type FakeCgcReader struct { 18 | io.ReaderAt 19 | first bool 20 | } 21 | 22 | func (f *FakeCgcReader) ReadAt(p []byte, off int64) (int, error) { 23 | n := 0 24 | if off < 4 && f.first { 25 | f.first = false 26 | n, _ = elfMagicReader.ReadAt(p, off) 27 | if n == len(p) { 28 | return n, nil 29 | } 30 | p = p[n:] 31 | off = 4 32 | } 33 | n1, err := f.ReaderAt.ReadAt(p, off) 34 | return n1 + n, err 35 | } 36 | 37 | type CgcLoader struct { 38 | models.Loader 39 | } 40 | 41 | func (c *CgcLoader) OS() string { 42 | return "cgc" 43 | } 44 | 45 | func NewCgcLoader(r io.ReaderAt, arch string) (models.Loader, error) { 46 | l, err := NewElfLoader(&FakeCgcReader{r, true}, arch) 47 | return &CgcLoader{l}, err 48 | } 49 | -------------------------------------------------------------------------------- /go/kernel/linux/info_linux.go: -------------------------------------------------------------------------------- 1 | package linux 2 | 3 | import ( 4 | "github.com/lunixbochs/struc" 5 | "syscall" 6 | 7 | co "github.com/lunixbochs/usercorn/go/kernel/common" 8 | ) 9 | 10 | func (k *LinuxKernel) Sysinfo(buf co.Obuf) uint64 { 11 | var tmp syscall.Sysinfo_t 12 | err := syscall.Sysinfo(&tmp) 13 | if err != nil { 14 | return UINT64_MAX // FIXME 15 | } 16 | info := Sysinfo_t{ 17 | Uptime: struc.Off_t(tmp.Uptime), 18 | Loads: [3]struc.Size_t{struc.Size_t(tmp.Loads[0]), struc.Size_t(tmp.Loads[1]), struc.Size_t(tmp.Loads[2])}, 19 | Totalram: struc.Size_t(tmp.Totalram), 20 | Freeram: struc.Size_t(tmp.Freeram), 21 | Sharedram: struc.Size_t(tmp.Sharedram), 22 | Bufferram: struc.Size_t(tmp.Bufferram), 23 | Totalswap: struc.Size_t(tmp.Totalswap), 24 | Freeswap: struc.Size_t(tmp.Freeswap), 25 | Procs: tmp.Procs, 26 | Totalhigh: struc.Size_t(tmp.Totalhigh), 27 | Freehigh: struc.Size_t(tmp.Freehigh), 28 | Unit: tmp.Unit, 29 | } 30 | if err := buf.Pack(&info); err != nil { 31 | return UINT64_MAX // FIXME 32 | } 33 | return 0 34 | } 35 | -------------------------------------------------------------------------------- /go/kernel/darwin/unpack.go: -------------------------------------------------------------------------------- 1 | package darwin 2 | 3 | import ( 4 | "github.com/lunixbochs/argjoy" 5 | "syscall" 6 | 7 | co "github.com/lunixbochs/usercorn/go/kernel/common" 8 | "github.com/lunixbochs/usercorn/go/kernel/darwin/unpack" 9 | "github.com/lunixbochs/usercorn/go/native/enum" 10 | ) 11 | 12 | func Unpack(k co.Kernel, arg interface{}, vals []interface{}) error { 13 | // TODO: this is the exact same preamble as linux 14 | reg0 := vals[0].(uint64) 15 | // null pointer guard 16 | if reg0 == 0 { 17 | return nil 18 | } 19 | buf := co.NewBuf(k, reg0) 20 | switch v := arg.(type) { 21 | case *syscall.Sockaddr: 22 | *v = unpack.Sockaddr(buf, int(vals[1].(uint64))) 23 | case *enum.OpenFlag: 24 | *v = unpack.OpenFlag(reg0) 25 | case *enum.MmapFlag: 26 | *v = unpack.MmapFlag(reg0) 27 | case *enum.MmapProt: 28 | *v = unpack.MmapProt(reg0) 29 | default: 30 | return argjoy.NoMatch 31 | } 32 | return nil 33 | } 34 | 35 | func registerUnpack(d *DarwinKernel) { 36 | d.Argjoy.Register(func(arg interface{}, vals []interface{}) error { 37 | return Unpack(d, arg, vals) 38 | }) 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Ryan Hileman 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /go/kernel/mach/mman.go: -------------------------------------------------------------------------------- 1 | package mach 2 | 3 | import ( 4 | co "github.com/lunixbochs/usercorn/go/kernel/common" 5 | "github.com/lunixbochs/usercorn/go/kernel/posix" 6 | ) 7 | 8 | func (k *MachKernel) KernelrpcMachVmAllocateTrap(target int, addrOut co.Obuf, size co.Len, flags int) uint64 { 9 | addr, err := k.U.Malloc(uint64(size), "mach_vm_allocate") 10 | if err != nil { 11 | return posix.UINT64_MAX // FIXME 12 | } 13 | if err := addrOut.Pack(addr); err != nil { 14 | return posix.UINT64_MAX // FIXME 15 | } 16 | return 0 17 | } 18 | 19 | func (k *MachKernel) KernelrpcMachVmDeallocateTrap(target int, addr co.Buf, size co.Len) uint64 { 20 | //TODO: implement 21 | return 0 22 | } 23 | 24 | func (k *MachKernel) KernelrpcMachVmMapTrap(target_mask uint32, addrOut co.Obuf, size uint64, mask uint64, flags int64, cur_prot uint64) uint64 { 25 | //TODO: implement prot/flags handling 26 | addr, err := k.U.Malloc(uint64(size), "mach_vm_map") 27 | if err != nil { 28 | return posix.UINT64_MAX // FIXME 29 | } 30 | if err := addrOut.Pack(addr); err != nil { 31 | return posix.UINT64_MAX // FIXME 32 | } 33 | return 0 34 | } 35 | -------------------------------------------------------------------------------- /go/models/cpu/pack_uint.go: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import ( 4 | "encoding/binary" 5 | "github.com/pkg/errors" 6 | ) 7 | 8 | func PackUint(order binary.ByteOrder, size int, buf []byte, n uint64) ([]byte, error) { 9 | if buf == nil { 10 | buf = make([]byte, size) 11 | } else if len(buf) < size { 12 | return nil, errors.Errorf("buffer too small (%d < %d)", len(buf), size) 13 | } 14 | switch size { 15 | case 8: 16 | order.PutUint64(buf[:size], n) 17 | case 4: 18 | order.PutUint32(buf[:size], uint32(n)) 19 | case 2: 20 | order.PutUint16(buf[:size], uint16(n)) 21 | case 1: 22 | buf[0] = byte(n) 23 | default: 24 | return nil, errors.Errorf("unsupported uint size: %d", size) 25 | } 26 | return buf[:size], nil 27 | } 28 | 29 | func UnpackUint(order binary.ByteOrder, size int, buf []byte) (uint64, error) { 30 | switch size { 31 | case 8: 32 | return order.Uint64(buf), nil 33 | case 4: 34 | return uint64(order.Uint32(buf)), nil 35 | case 2: 36 | return uint64(order.Uint16(buf)), nil 37 | case 1: 38 | return uint64(buf[0]), nil 39 | default: 40 | return 0, errors.Errorf("unsupported uint size: %d", size) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /go/kernel/common/args.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "github.com/lunixbochs/usercorn/go/models" 5 | ) 6 | 7 | func StackArgs(u models.Usercorn) func(n int) ([]uint64, error) { 8 | return func(n int) ([]uint64, error) { 9 | sp, _ := u.RegRead(u.Arch().SP) 10 | // starts with an empty slot 11 | s := u.StrucAt(sp + uint64(u.Bits()/8)) 12 | 13 | ret := make([]uint64, n) 14 | for i := 0; i < n; i++ { 15 | var arg uint64 16 | // TODO: simplify this when struc issue #47 is fixed 17 | if u.Bits() == 64 { 18 | s.Unpack(&arg) 19 | } else { 20 | var arg32 uint32 21 | s.Unpack(&arg32) 22 | arg = uint64(arg32) 23 | } 24 | if s.Error != nil { 25 | return nil, s.Error 26 | } 27 | ret[i] = arg 28 | } 29 | return ret, nil 30 | } 31 | } 32 | 33 | func RegArgs(u models.Usercorn, regs []int) func(n int) ([]uint64, error) { 34 | return func(n int) ([]uint64, error) { 35 | vals := make([]uint64, n) 36 | for i, enum := range regs[:n] { 37 | val, err := u.RegRead(enum) 38 | if err != nil { 39 | return nil, err 40 | } 41 | vals[i] = val 42 | } 43 | return vals[:n], nil 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /go/arch/arch.go: -------------------------------------------------------------------------------- 1 | package arch 2 | 3 | import ( 4 | "github.com/pkg/errors" 5 | 6 | "github.com/lunixbochs/usercorn/go/arch/arm" 7 | "github.com/lunixbochs/usercorn/go/arch/arm64" 8 | "github.com/lunixbochs/usercorn/go/arch/m68k" 9 | "github.com/lunixbochs/usercorn/go/arch/mips" 10 | "github.com/lunixbochs/usercorn/go/arch/sparc" 11 | "github.com/lunixbochs/usercorn/go/arch/x86" 12 | "github.com/lunixbochs/usercorn/go/arch/x86_16" 13 | "github.com/lunixbochs/usercorn/go/arch/x86_64" 14 | "github.com/lunixbochs/usercorn/go/models" 15 | ) 16 | 17 | var archMap = map[string]*models.Arch{ 18 | "arm": arm.Arch, 19 | "arm64": arm64.Arch, 20 | "m68k": m68k.Arch, 21 | "mips": mips.Arch, 22 | "sparc": sparc.Arch, 23 | "x86": x86.Arch, 24 | "x86_16": x86_16.Arch, 25 | "x86_64": x86_64.Arch, 26 | } 27 | 28 | func GetArch(name, os string) (*models.Arch, *models.OS, error) { 29 | a, ok := archMap[name] 30 | if !ok { 31 | return nil, nil, errors.Errorf("Arch '%s' not found.", name) 32 | } 33 | o, ok := a.OS[os] 34 | if !ok { 35 | return nil, nil, errors.Errorf("OS '%s' not found for arch '%s'.", os, name) 36 | } 37 | return a, o, nil 38 | } 39 | -------------------------------------------------------------------------------- /go/cmd/cfg/transaction.go: -------------------------------------------------------------------------------- 1 | package cfg 2 | 3 | import ( 4 | uc "github.com/unicorn-engine/unicorn/bindings/go/unicorn" 5 | 6 | "github.com/lunixbochs/usercorn/go/models" 7 | "github.com/lunixbochs/usercorn/go/models/cpu" 8 | ) 9 | 10 | type Transaction struct { 11 | ctx interface{} 12 | u models.Usercorn 13 | hh cpu.Hook 14 | 15 | memWrites map[uint64]byte 16 | } 17 | 18 | func NewTransaction(u models.Usercorn) *Transaction { 19 | ctx, _ := u.ContextSave(nil) 20 | memWrites := make(map[uint64]byte) 21 | hh, _ := u.HookAdd(uc.HOOK_MEM_WRITE, func(_ cpu.Cpu, addr uint64, size uint32) { 22 | data, _ := u.MemRead(addr, uint64(size)) 23 | for i, b := range data { 24 | pos := addr + uint64(i) 25 | if _, ok := memWrites[pos]; !ok { 26 | memWrites[pos] = b 27 | } 28 | } 29 | }, 1, 0) 30 | return &Transaction{ 31 | u: u, 32 | ctx: ctx, 33 | hh: hh, 34 | 35 | memWrites: memWrites, 36 | } 37 | } 38 | 39 | func (t *Transaction) Rewind() { 40 | t.u.ContextRestore(t.ctx) 41 | for addr, b := range t.memWrites { 42 | t.u.MemWrite(addr, []byte{b}) 43 | } 44 | } 45 | 46 | func (t *Transaction) Discard() { 47 | t.u.HookDel(t.hh) 48 | } 49 | -------------------------------------------------------------------------------- /go/cmd/cfg/arch.go: -------------------------------------------------------------------------------- 1 | package cfg 2 | 3 | /* 4 | import ( 5 | cs "github.com/bnagy/gapstone" 6 | 7 | "github.com/lunixbochs/usercorn/go/models" 8 | ) 9 | 10 | type archConfig struct { 11 | CondJmps []int 12 | Jmps []int 13 | Call int 14 | Ret int 15 | } 16 | 17 | func ArchConfig(u models.Usercorn) *archConfig { 18 | switch u.Arch().Radare { 19 | case "x86", "x86_64": 20 | condJmps := []int{ 21 | cs.X86_INS_JA, 22 | cs.X86_INS_JAE, 23 | cs.X86_INS_JB, 24 | cs.X86_INS_JBE, 25 | cs.X86_INS_JCXZ, 26 | cs.X86_INS_JE, 27 | cs.X86_INS_JECXZ, 28 | cs.X86_INS_JG, 29 | cs.X86_INS_JGE, 30 | cs.X86_INS_JL, 31 | cs.X86_INS_JLE, 32 | cs.X86_INS_JNE, 33 | cs.X86_INS_JNO, 34 | cs.X86_INS_JNP, 35 | cs.X86_INS_JNS, 36 | cs.X86_INS_JO, 37 | cs.X86_INS_JP, 38 | cs.X86_INS_JRCXZ, 39 | cs.X86_INS_JS, 40 | } 41 | jmps := []int{ 42 | cs.X86_INS_JMP, 43 | cs.X86_INS_LOOP, 44 | cs.X86_INS_LOOPE, 45 | cs.X86_INS_LOOPNE, 46 | } 47 | return &archConfig{ 48 | CondJmps: condJmps, 49 | Jmps: jmps, 50 | Call: cs.X86_INS_CALL, 51 | Ret: cs.X86_INS_RET, 52 | } 53 | default: 54 | panic("unknown arch") 55 | } 56 | } 57 | */ 58 | -------------------------------------------------------------------------------- /go/native/statfs_linux.go: -------------------------------------------------------------------------------- 1 | package native 2 | 3 | import ( 4 | "syscall" 5 | ) 6 | 7 | func StatfsToLinux(s *syscall.Statfs_t) *LinuxStatfs_t { 8 | return &LinuxStatfs_t{ 9 | Type: int64(s.Type), 10 | Bsize: int64(s.Bsize), 11 | Blocks: s.Blocks, 12 | Bfree: s.Bfree, 13 | Bavail: s.Bavail, 14 | Files: s.Files, 15 | Ffree: s.Ffree, 16 | Fsid: s.Fsid, 17 | Namelen: int64(s.Namelen), 18 | Frsize: int64(s.Frsize), 19 | Flags: int64(s.Flags), 20 | } 21 | } 22 | 23 | func StatfsToDarwin32(s *syscall.Statfs_t) *DarwinStatfs32_t { 24 | // TODO: fs type, owner, mnt names? 25 | return &DarwinStatfs32_t{ 26 | Bsize: int(s.Bsize), 27 | Blocks: int(s.Blocks), 28 | Bfree: int(s.Bfree), 29 | Bavail: int(s.Bavail), 30 | Files: int(s.Files), 31 | Ffree: int(s.Ffree), 32 | Fsid: s.Fsid, 33 | } 34 | } 35 | 36 | func StatfsToDarwin64(s *syscall.Statfs_t) *DarwinStatfs64_t { 37 | // TODO: fs type, owner, mnt names? 38 | return &DarwinStatfs64_t{ 39 | Bsize: uint32(s.Bsize), 40 | Blocks: uint64(s.Blocks), 41 | Bfree: uint64(s.Bfree), 42 | Bavail: uint64(s.Bavail), 43 | Files: uint64(s.Files), 44 | Ffree: uint64(s.Ffree), 45 | Fsid: s.Fsid, 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /go/kernel/common/types.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "github.com/lunixbochs/argjoy" 5 | "github.com/pkg/errors" 6 | "os" 7 | 8 | "github.com/lunixbochs/usercorn/go/models" 9 | ) 10 | 11 | type ( 12 | Buf struct { 13 | Addr uint64 14 | K *KernelBase 15 | } 16 | Obuf struct{ Buf } 17 | Len uint64 18 | Off int64 19 | Fd int32 20 | Ptr uint64 21 | ) 22 | 23 | func NewBuf(k Kernel, addr uint64) Buf { 24 | return Buf{K: k.UsercornKernel(), Addr: addr} 25 | } 26 | 27 | func (b Buf) Struc() *models.StrucStream { 28 | return b.K.U.StrucAt(b.Addr) 29 | } 30 | 31 | func (b Buf) Pack(i interface{}) error { 32 | if b.K.Pack != nil { 33 | if err := b.K.Pack(b, i); err == nil { 34 | return nil 35 | } else if err != argjoy.NoMatch { 36 | return err 37 | } 38 | } 39 | return errors.Wrap(b.Struc().Pack(i), "struc.Pack() failed") 40 | } 41 | 42 | func (b Buf) Unpack(i interface{}) error { 43 | return errors.Wrap(b.Struc().Unpack(i), "struc.Unpack() failed") 44 | } 45 | 46 | func (b Buf) Sizeof(i interface{}) (int, error) { 47 | n, err := b.Struc().Sizeof(i) 48 | return n, errors.Wrap(err, "struc.Sizeof() failed") 49 | } 50 | 51 | func (f Fd) File() *os.File { 52 | return os.NewFile(uintptr(f), "") 53 | } 54 | -------------------------------------------------------------------------------- /go/models/callstack.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type Stackframe struct { 4 | PC, SP uint64 5 | } 6 | 7 | type Callstack struct { 8 | Stack []Stackframe 9 | } 10 | 11 | func (s *Callstack) Len() int { 12 | return len(s.Stack) 13 | } 14 | 15 | func (s *Callstack) Freeze(pc, sp uint64) []Stackframe { 16 | if s.Empty() || s.Peek().PC != pc { 17 | return append(s.Stack, Stackframe{pc, sp}) 18 | } 19 | return s.Stack 20 | } 21 | 22 | func (s *Callstack) Push(pc, sp uint64) { 23 | s.Stack = append(s.Stack, Stackframe{pc, sp}) 24 | } 25 | 26 | func (s *Callstack) Empty() bool { 27 | return s.Len() == 0 28 | } 29 | 30 | func (s *Callstack) Peek() Stackframe { 31 | if s.Empty() { 32 | return Stackframe{} 33 | } 34 | return s.Stack[s.Len()-1] 35 | } 36 | 37 | func (s *Callstack) Pop() Stackframe { 38 | if s.Empty() { 39 | return Stackframe{} 40 | } 41 | ret := s.Peek() 42 | s.Stack = s.Stack[:s.Len()-1] 43 | return ret 44 | } 45 | 46 | func (s *Callstack) Update(pc, sp uint64) { 47 | if s.Empty() || sp < s.Peek().SP { 48 | s.Push(pc, sp) 49 | } else { 50 | if sp == s.Peek().SP { 51 | s.Stack[s.Len()-1].PC = pc 52 | } else { 53 | for !s.Empty() && sp > s.Peek().SP { 54 | s.Pop() 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /go/kernel/posix/user.go: -------------------------------------------------------------------------------- 1 | package posix 2 | 3 | import ( 4 | "os" 5 | "syscall" 6 | 7 | co "github.com/lunixbochs/usercorn/go/kernel/common" 8 | ) 9 | 10 | func (k *PosixKernel) Getegid() int { 11 | return os.Getegid() 12 | } 13 | 14 | func (k *PosixKernel) Getgid() int { 15 | return os.Getgid() 16 | } 17 | 18 | func (k *PosixKernel) Geteuid() int { 19 | return os.Geteuid() 20 | } 21 | 22 | func (k *PosixKernel) Getuid() int { 23 | return os.Getuid() 24 | } 25 | 26 | func (k *PosixKernel) Setgid(gid int) int { 27 | // TODO: doesn't work on Linux 28 | syscall.Setgid(gid) 29 | return 0 30 | } 31 | 32 | func (k *PosixKernel) Setuid(uid int) int { 33 | // TODO: doesn't work on Linux 34 | syscall.Setuid(uid) 35 | return 0 36 | } 37 | 38 | func (k *PosixKernel) Getgroups(count int, buf co.Buf) uint64 { 39 | groups, err := syscall.Getgroups() 40 | if err != nil { 41 | return Errno(err) 42 | } 43 | length := uint64(len(groups)) 44 | if count > 0 { 45 | if count < len(groups) { 46 | groups = groups[:count] 47 | } 48 | tmp := make([]uint32, len(groups)) 49 | for i, v := range groups { 50 | tmp[i] = uint32(v) 51 | } 52 | if err := buf.Pack(tmp); err != nil { 53 | return UINT64_MAX // FIXME 54 | } 55 | } 56 | return length 57 | } 58 | -------------------------------------------------------------------------------- /go/models/cpu/page_test.go: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func page_eq(a Pages, b Pages) bool { 8 | if len(a) != len(b) { 9 | return false 10 | } 11 | for i := range a { 12 | if a[i] != b[i] { 13 | return false 14 | } 15 | } 16 | return true 17 | } 18 | 19 | func TestPageFind(t *testing.T) { 20 | mem := Pages{ 21 | &Page{Addr: 0x1000, Size: 0x1000}, 22 | &Page{Addr: 0x2000, Size: 0x1000}, 23 | &Page{Addr: 0x4000, Size: 0x2000}, 24 | &Page{Addr: 0x6000, Size: 0x2000}, 25 | } 26 | if mem.Find(0x1000) != mem[0] || 27 | mem.Find(0x1001) != mem[0] || 28 | mem.Find(0x1fff) != mem[0] { 29 | t.Error("Find() failed") 30 | } 31 | if mem.Find(0x3000) != nil || 32 | mem.Find(0x1) != nil || 33 | mem.Find(0x10000) != nil { 34 | t.Error("Find() negative failed") 35 | } 36 | if !page_eq(mem.FindRange(0x0, 0x10000), mem) || 37 | !page_eq(mem.FindRange(0x0, 0x1000), nil) || 38 | !page_eq(mem.FindRange(0x1000, 0x1000), mem[:1]) || 39 | !page_eq(mem.FindRange(0x1000, 0x2000), mem[:2]) || 40 | !page_eq(mem.FindRange(0x2000, 0x2000), mem[1:2]) || 41 | !page_eq(mem.FindRange(0x2000, 0x4000), mem[1:3]) || 42 | !page_eq(mem.FindRange(0x2000, 0x10000), mem[1:]) { 43 | t.Error("FindRange() failed") 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /go/kernel/redox/enum.go: -------------------------------------------------------------------------------- 1 | package redox 2 | 3 | // these numbers are from https://github.com/redox-os/redox/blob/master/crates/system/syscall/unix.rs 4 | var RedoxSysNum = map[int]string{ 5 | 1: "exit", 6 | 3: "read", 7 | 4: "write", 8 | 5: "open", 9 | 6: "close", 10 | 7: "waitpid", 11 | 9: "link", 12 | 10: "unlink", 13 | 11: "execve", 14 | 12: "chdir", 15 | 18: "stat", 16 | 19: "lseek", 17 | 20: "getpid", 18 | 28: "fstat", 19 | 39: "mkdir", 20 | 41: "dup", 21 | 45: "brk", 22 | 84: "rmdir", 23 | 93: "ftruncate", 24 | 118: "fsync", 25 | 120: "clone", 26 | 158: "yield", 27 | 162: "nanosleep", 28 | 265: "clock_gettime", 29 | 331: "pipe2", 30 | 928: "fpath", 31 | } 32 | 33 | const ( 34 | CLONE_VM = 0x100 35 | CLONE_FS = 0x200 36 | CLONE_FILES = 0x400 37 | CLONE_VFORK = 0x4000 38 | 39 | CLOCK_REALTIME = 1 40 | CLOCK_MONOTONIC = 4 41 | 42 | SEEK_SET = 0 43 | SEEK_CUR = 1 44 | SEEK_END = 2 45 | 46 | O_RDONLY = 0 47 | O_WRONLY = 1 48 | O_RDWR = 2 49 | O_NONBLOCK = 4 50 | O_APPEND = 8 51 | O_SHLOCK = 0x10 52 | O_EXLOCK = 0x20 53 | O_ASYNC = 0x40 54 | O_FSYNC = 0x80 55 | O_CREAT = 0x200 56 | O_TRUNC = 0x400 57 | O_EXCL = 0x800 58 | 59 | MODE_DIR = 0x4000 60 | MODE_FILE = 0x8000 61 | ) 62 | -------------------------------------------------------------------------------- /go/models/cli.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "strings" 7 | ) 8 | 9 | func PrintFlags(flags []*flag.Flag) { 10 | wname := 0 11 | wdef := 0 12 | for _, f := range flags { 13 | if len(f.Name) > wname { 14 | wname = len(f.Name) 15 | } 16 | if len(f.DefValue) > wdef { 17 | wdef = len(f.DefValue) 18 | } 19 | } 20 | wdesc := 80 - wname - wdef - 7 21 | 22 | namefmt := fmt.Sprintf("%%-%ds", wname) 23 | deffmt := fmt.Sprintf("%%-%ds ", wdef+2) 24 | lpad := strings.Repeat(" ", wname+wdef+7) 25 | for _, f := range flags { 26 | fmt.Printf(" -"+namefmt, f.Name) 27 | if f.DefValue != "" && f.DefValue != "[]" { 28 | fmt.Printf(" "+deffmt, "("+f.DefValue+")") 29 | } else { 30 | fmt.Printf(" "+deffmt, " ") 31 | } 32 | for i := 0; i < len(f.Usage); { 33 | if i > 0 { 34 | fmt.Printf("%s", lpad) 35 | } 36 | l := wdesc 37 | skip := false 38 | if i+wdesc > len(f.Usage) { 39 | l = len(f.Usage) - i 40 | } else { 41 | // split on newline or space if present 42 | s := strings.LastIndexAny(f.Usage[i:i+l], " \n") 43 | if s > 0 { 44 | l = s 45 | skip = true 46 | } 47 | } 48 | fmt.Printf("%s\n", f.Usage[i:i+l]) 49 | i += l 50 | if skip { 51 | i += 1 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /go/arch/x86_16/arch.go: -------------------------------------------------------------------------------- 1 | package x86_16 2 | 3 | import ( 4 | ks "github.com/keystone-engine/keystone/bindings/go/keystone" 5 | cs "github.com/lunixbochs/capstr" 6 | uc "github.com/unicorn-engine/unicorn/bindings/go/unicorn" 7 | 8 | "github.com/lunixbochs/usercorn/go/cpu" 9 | "github.com/lunixbochs/usercorn/go/cpu/unicorn" 10 | "github.com/lunixbochs/usercorn/go/models" 11 | ) 12 | 13 | var Arch = &models.Arch{ 14 | Name: "x86_16", 15 | Bits: 16, 16 | Radare: "x86", 17 | 18 | Cpu: &unicorn.Builder{Arch: uc.ARCH_X86, Mode: uc.MODE_16}, 19 | Dis: &cpu.Capstr{Arch: cs.ARCH_X86, Mode: cs.MODE_16}, 20 | Asm: &cpu.Keystone{Arch: ks.ARCH_X86, Mode: ks.MODE_16}, 21 | 22 | PC: uc.X86_REG_IP, 23 | SP: uc.X86_REG_SP, 24 | Regs: map[string]int{ 25 | "ip": uc.X86_REG_IP, 26 | "sp": uc.X86_REG_SP, 27 | "bp": uc.X86_REG_BP, 28 | "ax": uc.X86_REG_AX, 29 | "bx": uc.X86_REG_BX, 30 | "cx": uc.X86_REG_CX, 31 | "dx": uc.X86_REG_DX, 32 | "si": uc.X86_REG_SI, 33 | "di": uc.X86_REG_DI, 34 | 35 | "flags": uc.X86_REG_EFLAGS, 36 | 37 | "cs": uc.X86_REG_CS, 38 | "ds": uc.X86_REG_DS, 39 | "es": uc.X86_REG_ES, 40 | "ss": uc.X86_REG_SS, 41 | }, 42 | DefaultRegs: []string{ 43 | "ax", "bx", "cx", "dx", "si", "di", "bp", 44 | }, 45 | GdbXml: "", // TODO: Find this GDB xml for x86_16 46 | } 47 | -------------------------------------------------------------------------------- /go/cmd/launcher.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | ) 8 | 9 | type command struct { 10 | name, desc string 11 | main func(args []string) 12 | } 13 | 14 | var commands map[string]*command 15 | var order []string 16 | var pad int 17 | 18 | func init() { commands = make(map[string]*command) } 19 | 20 | func Register(name, desc string, main func(args []string)) { 21 | if len(name) > pad { 22 | pad = len(name) 23 | } 24 | commands[name] = &command{name, desc, main} 25 | order = append(order, name) 26 | } 27 | 28 | func Main() { 29 | usage := func() { 30 | fmt.Fprintln(os.Stderr, "Commands:") 31 | fstr := fmt.Sprintf("%%-%ds | %%s\n", pad) 32 | for _, name := range order { 33 | cmd := commands[name] 34 | fmt.Fprintf(os.Stderr, fstr, cmd.name, cmd.desc) 35 | } 36 | fmt.Fprintf(os.Stderr, "\nExample: %s run -trace -symfile bins/x86_64.linux.elf\n\n", os.Args[0]) 37 | } 38 | if len(os.Args) < 2 { 39 | usage() 40 | os.Exit(1) 41 | } else { 42 | cmd, ok := commands[os.Args[1]] 43 | if ok { 44 | args := append([]string{strings.Join(os.Args[:2], " ")}, os.Args[2:]...) 45 | cmd.main(args) 46 | } else { 47 | fmt.Fprintf(os.Stderr, "Command '%s' not found.\n\n", os.Args[1]) 48 | usage() 49 | os.Exit(1) 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /go/cmd/rawcmd.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "encoding/binary" 5 | "github.com/pkg/errors" 6 | 7 | "github.com/lunixbochs/usercorn/go" 8 | "github.com/lunixbochs/usercorn/go/loader" 9 | "github.com/lunixbochs/usercorn/go/models" 10 | ) 11 | 12 | func NewUsercornRawCmd() *UsercornCmd { 13 | c := NewUsercornCmd() 14 | 15 | var entry *uint64 16 | var arch, osStr, endian *string 17 | c.MakeUsercorn = func(exe string) (models.Usercorn, error) { 18 | var byteOrder binary.ByteOrder 19 | switch *endian { 20 | case "little": 21 | byteOrder = binary.LittleEndian 22 | case "big": 23 | byteOrder = binary.BigEndian 24 | default: 25 | return nil, errors.Errorf("%s is not a valid byte order ('little' or 'big')", *endian) 26 | } 27 | var err error 28 | l := loader.NewNullLoader(*arch, *osStr, byteOrder, *entry) 29 | u, err := usercorn.NewUsercornRaw(l, c.Config) 30 | if err != nil { 31 | return nil, err 32 | } 33 | u.SetEntry(*entry) 34 | return u, nil 35 | } 36 | c.SetupFlags = func() error { 37 | entry = c.Flags.Uint64("entry", 0, "entry point") 38 | arch = c.Flags.String("arch", "x86", "target architecture") 39 | osStr = c.Flags.String("os", "linux", "target OS") 40 | endian = c.Flags.String("endian", "little", "'big' or 'little' endian") 41 | return nil 42 | } 43 | return c 44 | } 45 | -------------------------------------------------------------------------------- /go/cmd/shellcode/shellcode.go: -------------------------------------------------------------------------------- 1 | package shellcode 2 | 3 | import ( 4 | "encoding/hex" 5 | "github.com/pkg/errors" 6 | "io/ioutil" 7 | "os" 8 | 9 | "github.com/lunixbochs/usercorn/go/cmd" 10 | "github.com/lunixbochs/usercorn/go/models" 11 | "github.com/lunixbochs/usercorn/go/models/cpu" 12 | ) 13 | 14 | func Main(args []string) { 15 | c := cmd.NewUsercornRawCmd() 16 | c.NoArgs = true 17 | 18 | var shellcode []byte 19 | oldMake := c.MakeUsercorn 20 | c.MakeUsercorn = func(exe string) (models.Usercorn, error) { 21 | var err error 22 | if exe == "-" { 23 | shellcode, err = ioutil.ReadAll(os.Stdin) 24 | } else { 25 | shellcode, err = hex.DecodeString(exe) 26 | } 27 | if err != nil { 28 | return nil, errors.Wrap(err, "failed to read shellcode") 29 | } 30 | return oldMake(exe) 31 | } 32 | c.RunUsercorn = func() error { 33 | u := c.Usercorn 34 | size := uint64(len(shellcode)) 35 | addr, err := u.Mmap(u.Entry(), size, cpu.PROT_ALL, false, "shellcode", nil) 36 | if err != nil { 37 | return err 38 | } 39 | if err := u.MemWrite(addr, shellcode); err != nil { 40 | return err 41 | } 42 | u.SetEntry(addr) 43 | u.SetExit(addr + size) 44 | return u.Run() 45 | } 46 | c.Run(args[1:], os.Environ()) 47 | } 48 | 49 | func init() { cmd.Register("shellcode", "execute a blob of machine code directly", Main) } 50 | -------------------------------------------------------------------------------- /go/arch/m68k/arch.go: -------------------------------------------------------------------------------- 1 | package m68k 2 | 3 | import ( 4 | // cs "github.com/lunixbochs/capstr" 5 | // ks "github.com/keystone-engine/keystone/bindings/go/keystone" 6 | uc "github.com/unicorn-engine/unicorn/bindings/go/unicorn" 7 | 8 | // "github.com/lunixbochs/usercorn/go/cpu" 9 | "github.com/lunixbochs/usercorn/go/cpu/unicorn" 10 | "github.com/lunixbochs/usercorn/go/models" 11 | ) 12 | 13 | var Arch = &models.Arch{ 14 | Name: "m68k", 15 | Bits: 32, 16 | Radare: "m68k", 17 | 18 | Cpu: &unicorn.Builder{Arch: uc.ARCH_M68K, Mode: uc.MODE_BIG_ENDIAN}, 19 | // no capstone support for m68k 20 | Asm: nil, 21 | Dis: nil, 22 | 23 | PC: uc.M68K_REG_PC, 24 | SP: uc.M68K_REG_A7, 25 | Regs: map[string]int{ 26 | "d0": uc.M68K_REG_D0, 27 | "d1": uc.M68K_REG_D1, 28 | "d2": uc.M68K_REG_D2, 29 | "d3": uc.M68K_REG_D3, 30 | "d4": uc.M68K_REG_D4, 31 | "d5": uc.M68K_REG_D5, 32 | "d6": uc.M68K_REG_D6, 33 | "d7": uc.M68K_REG_D7, 34 | "a0": uc.M68K_REG_A0, 35 | "a1": uc.M68K_REG_A1, 36 | "a2": uc.M68K_REG_A2, 37 | "a3": uc.M68K_REG_A3, 38 | "a4": uc.M68K_REG_A4, 39 | "a5": uc.M68K_REG_A5, 40 | "a6": uc.M68K_REG_A6, 41 | "sp": uc.M68K_REG_A7, 42 | "pc": uc.M68K_REG_PC, 43 | }, 44 | DefaultRegs: []string{ 45 | "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", 46 | "a0", "a1", "a2", "a3", "a4", "a5", "a6", 47 | }, 48 | } 49 | -------------------------------------------------------------------------------- /go/arch/sparc/linux.go: -------------------------------------------------------------------------------- 1 | package sparc 2 | 3 | import ( 4 | "fmt" 5 | "github.com/lunixbochs/ghostrace/ghost/sys/num" 6 | uc "github.com/unicorn-engine/unicorn/bindings/go/unicorn" 7 | 8 | co "github.com/lunixbochs/usercorn/go/kernel/common" 9 | "github.com/lunixbochs/usercorn/go/kernel/linux" 10 | "github.com/lunixbochs/usercorn/go/models" 11 | ) 12 | 13 | // TODO: sparc linux syscall abi regs 14 | var LinuxRegs = []int{} 15 | 16 | type LinuxKernel struct { 17 | *linux.LinuxKernel 18 | } 19 | 20 | func LinuxKernels(u models.Usercorn) []interface{} { 21 | kernel := &LinuxKernel{linux.NewKernel()} 22 | return []interface{}{kernel} 23 | } 24 | 25 | func LinuxSyscall(u models.Usercorn) { 26 | // TODO: handle errors or something 27 | g1, _ := u.RegRead(uc.SPARC_REG_G1) 28 | // TODO: add sparc x86 syscall numbers to ghostrace 29 | name, _ := num.Linux_x86[int(g1)] 30 | ret, _ := u.Syscall(int(g1), name, co.RegArgs(u, LinuxRegs)) 31 | u.RegWrite(uc.SPARC_REG_O0, ret) 32 | } 33 | 34 | // TODO: add sparc syscall convention support 35 | func LinuxInterrupt(u models.Usercorn, intno uint32) { 36 | panic(fmt.Sprintf("unknown interrupt: %d", intno)) 37 | } 38 | 39 | func init() { 40 | Arch.RegisterOS(&models.OS{ 41 | Name: "linux", 42 | Kernels: LinuxKernels, 43 | Init: linux.StackInit, 44 | Interrupt: LinuxInterrupt, 45 | }) 46 | } 47 | -------------------------------------------------------------------------------- /go/models/gate.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | type waiter struct { 8 | sync.Mutex 9 | cb []chan int 10 | } 11 | 12 | func (w *waiter) Add() chan int { 13 | w.Lock() 14 | ret := make(chan int) 15 | w.cb = append(w.cb, ret) 16 | w.Unlock() 17 | return ret 18 | } 19 | 20 | func (w *waiter) Truncate() { 21 | if w.cb != nil { 22 | w.cb = w.cb[:0] 23 | } 24 | } 25 | 26 | func (w *waiter) Notify() { 27 | w.Lock() 28 | for _, c := range w.cb { 29 | c <- 1 30 | } 31 | w.Truncate() 32 | w.Unlock() 33 | } 34 | 35 | type Gate struct { 36 | sync.Mutex 37 | wg sync.WaitGroup 38 | 39 | start, stop waiter 40 | } 41 | 42 | func (g *Gate) Start() { 43 | g.Lock() 44 | g.start.Notify() 45 | g.wg.Wait() 46 | } 47 | 48 | func (g *Gate) Stop() { 49 | g.stop.Notify() 50 | g.Unlock() 51 | g.wg.Wait() 52 | } 53 | 54 | func (g *Gate) StopLock() { 55 | g.wg.Add(1) 56 | <-g.stop.Add() 57 | g.Lock() 58 | g.wg.Done() 59 | } 60 | 61 | func (g *Gate) UnlockStart() { 62 | block := g.start.Add() 63 | g.Unlock() 64 | <-block 65 | } 66 | 67 | func (g *Gate) UnlockStop() { 68 | block := g.stop.Add() 69 | g.Unlock() 70 | <-block 71 | } 72 | 73 | func (g *Gate) UnlockStopRelock() { 74 | start := g.stop.Add() 75 | stop := g.stop.Add() 76 | g.Unlock() 77 | <-start 78 | g.wg.Add(1) 79 | <-stop 80 | g.Lock() 81 | g.wg.Done() 82 | } 83 | -------------------------------------------------------------------------------- /go/lua/bind_cpu.go: -------------------------------------------------------------------------------- 1 | package lua 2 | 3 | import ( 4 | "github.com/lunixbochs/usercorn/go/models/cpu" 5 | 6 | "github.com/lunixbochs/luaish" 7 | ) 8 | 9 | var cpuEnums = map[string]lua.LInt{ 10 | "HOOK_INTR": cpu.HOOK_INTR, 11 | "HOOK_INSN": cpu.HOOK_INSN, 12 | "HOOK_CODE": cpu.HOOK_CODE, 13 | "HOOK_BLOCK": cpu.HOOK_BLOCK, 14 | "HOOK_MEM_READ": cpu.HOOK_MEM_READ, 15 | "HOOK_MEM_WRITE": cpu.HOOK_MEM_WRITE, 16 | "HOOK_MEM_ERR": cpu.HOOK_MEM_ERR, 17 | 18 | "MEM_WRITE_UNMAPPED": cpu.MEM_WRITE_UNMAPPED, 19 | "MEM_READ_UNMAPPED": cpu.MEM_READ_UNMAPPED, 20 | "MEM_FETCH_UNMAPPED": cpu.MEM_FETCH_UNMAPPED, 21 | "MEM_WRITE_PROT": cpu.MEM_WRITE_PROT, 22 | "MEM_READ_PROT": cpu.MEM_READ_PROT, 23 | "MEM_FETCH_PROT": cpu.MEM_FETCH_PROT, 24 | 25 | "MEM_PROT": cpu.MEM_PROT, 26 | "MEM_UNMAPPED": cpu.MEM_UNMAPPED, 27 | "PROT_NONE": cpu.PROT_NONE, 28 | "PROT_READ": cpu.PROT_READ, 29 | "PROT_WRITE": cpu.PROT_WRITE, 30 | "PROT_EXEC": cpu.PROT_EXEC, 31 | "PROT_ALL": cpu.PROT_ALL, 32 | 33 | "MEM_WRITE": cpu.MEM_WRITE, 34 | "MEM_READ": cpu.MEM_READ, 35 | "MEM_FETCH": cpu.MEM_FETCH, 36 | } 37 | 38 | // this injects enums from models/cpu 39 | func bindCpu(L *LuaRepl) error { 40 | mod := L.NewTable() 41 | for k, v := range cpuEnums { 42 | mod.RawSetString(k, v) 43 | } 44 | L.SetGlobal("cpu", mod) 45 | return nil 46 | } 47 | -------------------------------------------------------------------------------- /go/kernel/darwin/io.go: -------------------------------------------------------------------------------- 1 | package darwin 2 | 3 | import ( 4 | co "github.com/lunixbochs/usercorn/go/kernel/common" 5 | "github.com/lunixbochs/usercorn/go/native/enum" 6 | ) 7 | 8 | func (k *DarwinKernel) Pread(fd co.Fd, buf co.Obuf, size co.Len, offset int64) uint64 { 9 | return k.PosixKernel.Pread64(fd, buf, size, offset) 10 | } 11 | 12 | func (k *DarwinKernel) PreadNocancel(fd co.Fd, buf co.Obuf, size co.Len, offset int64) uint64 { 13 | // TODO: will it be possible to cancel syscalls here? 14 | // what conditions can cancel a syscall in the real world? 15 | return k.Pread(fd, buf, size, offset) 16 | } 17 | 18 | func (k *DarwinKernel) Pwrite(fd co.Fd, buf co.Buf, size co.Len, offset int64) uint64 { 19 | return k.PosixKernel.Pwrite64(fd, buf, size, offset) 20 | } 21 | 22 | func (k *DarwinKernel) Fstat64(fd co.Fd, buf co.Obuf) uint64 { 23 | return k.PosixKernel.Fstat(fd, buf) 24 | } 25 | 26 | func (k *DarwinKernel) Stat64(path string, buf co.Obuf) uint64 { 27 | return k.PosixKernel.Stat(path, buf) 28 | } 29 | 30 | func (k *DarwinKernel) OpenNocancel(path string, flags enum.OpenFlag, mode uint64) uint64 { 31 | return k.PosixKernel.Open(path, flags, mode) 32 | } 33 | 34 | func (k *DarwinKernel) ReadNocancel(fd co.Fd, buf co.Obuf, size co.Len) uint64 { 35 | return k.PosixKernel.Read(fd, buf, size) 36 | } 37 | 38 | func (k *DarwinKernel) CloseNocancel(fd co.Fd) uint64 { 39 | return k.PosixKernel.Close(fd) 40 | } 41 | -------------------------------------------------------------------------------- /go/arch/x86_64/arch.go: -------------------------------------------------------------------------------- 1 | package x86_64 2 | 3 | import ( 4 | ks "github.com/keystone-engine/keystone/bindings/go/keystone" 5 | cs "github.com/lunixbochs/capstr" 6 | uc "github.com/unicorn-engine/unicorn/bindings/go/unicorn" 7 | 8 | "github.com/lunixbochs/usercorn/go/cpu" 9 | "github.com/lunixbochs/usercorn/go/cpu/unicorn" 10 | "github.com/lunixbochs/usercorn/go/models" 11 | ) 12 | 13 | var Arch = &models.Arch{ 14 | Name: "x86_64", 15 | Bits: 64, 16 | Radare: "x86", 17 | 18 | Cpu: &unicorn.Builder{Arch: uc.ARCH_X86, Mode: uc.MODE_64}, 19 | Dis: &cpu.Capstr{Arch: cs.ARCH_X86, Mode: cs.MODE_64}, 20 | Asm: &cpu.Keystone{Arch: ks.ARCH_X86, Mode: ks.MODE_64}, 21 | 22 | PC: uc.X86_REG_RIP, 23 | SP: uc.X86_REG_RSP, 24 | Regs: map[string]int{ 25 | "rax": uc.X86_REG_RAX, 26 | "rbx": uc.X86_REG_RBX, 27 | "rcx": uc.X86_REG_RCX, 28 | "rdx": uc.X86_REG_RDX, 29 | "rsi": uc.X86_REG_RSI, 30 | "rdi": uc.X86_REG_RDI, 31 | "rbp": uc.X86_REG_RBP, 32 | "rsp": uc.X86_REG_RSP, 33 | "rip": uc.X86_REG_RIP, 34 | "r8": uc.X86_REG_R8, 35 | "r9": uc.X86_REG_R9, 36 | "r10": uc.X86_REG_R10, 37 | "r11": uc.X86_REG_R11, 38 | "r12": uc.X86_REG_R12, 39 | "r13": uc.X86_REG_R13, 40 | "r14": uc.X86_REG_R14, 41 | "r15": uc.X86_REG_R15, 42 | }, 43 | DefaultRegs: []string{ 44 | "rax", "rbx", "rcx", "rdx", "rsi", "rdi", "rbp", 45 | "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", 46 | }, 47 | GdbXml: gdbXml, 48 | } 49 | -------------------------------------------------------------------------------- /go/lua/bindings.go: -------------------------------------------------------------------------------- 1 | package lua 2 | 3 | import ( 4 | "github.com/lunixbochs/luaish" 5 | "strconv" 6 | ) 7 | 8 | func (L *LuaRepl) printFunc(_ *lua.LState) int { 9 | L.PrettyPrint(L.getArgs(), false) 10 | return 0 11 | } 12 | 13 | func (L *LuaRepl) intFunc(_ *lua.LState) int { 14 | switch v := L.CheckAny(1).(type) { 15 | case lua.LString: 16 | n, err := strconv.ParseInt(string(v), 0, 64) 17 | if err == nil { 18 | L.Push(lua.LInt(n)) 19 | return 1 20 | } 21 | case lua.LFloat: 22 | L.Push(lua.LInt(v)) 23 | return 1 24 | case lua.LInt: 25 | L.Push(v) 26 | return 1 27 | } 28 | return 0 29 | } 30 | 31 | func (L *LuaRepl) loadBindings() error { 32 | // populate predefined globals as "_builtins" so they can be skipped in help()/dir() 33 | builtins := L.NewTable() 34 | g := L.GetGlobal("_G") 35 | if s, ok := g.(*lua.LTable); ok { 36 | s.ForEach(func(k, v lua.LValue) { 37 | builtins.RawSet(k, lua.LTrue) 38 | }) 39 | } 40 | L.SetGlobal("_builtins", builtins) 41 | 42 | print := L.NewFunction(L.printFunc) 43 | L.SetGlobal("print", print) 44 | 45 | toint := L.NewFunction(L.intFunc) 46 | L.SetGlobal("int", toint) 47 | 48 | if err := bindCpu(L); err != nil { 49 | return err 50 | } else if err := bindUsercorn(L); err != nil { 51 | return err 52 | } else if err := L.DoString(sugarRc); err != nil { 53 | return err 54 | } else if err := L.DoString(cmdRc); err != nil { 55 | return err 56 | } 57 | return nil 58 | } 59 | -------------------------------------------------------------------------------- /go/cpu/capstr.go: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import ( 4 | cs "github.com/lunixbochs/capstr" 5 | "github.com/pkg/errors" 6 | 7 | "github.com/lunixbochs/usercorn/go/models" 8 | ) 9 | 10 | type Capstr struct { 11 | Arch, Mode int 12 | 13 | cs *cs.Engine 14 | // FIXME: there's a special case on every capstone just for thumb 15 | thumb *Capstr 16 | dc *models.Discache 17 | } 18 | 19 | func (c *Capstr) Open() (err error) { 20 | engine, err := cs.New(c.Arch, c.Mode) 21 | if err == nil { 22 | c.cs = engine 23 | c.dc = models.NewDiscache() 24 | } 25 | return errors.Wrap(err, "cs.New() failed") 26 | } 27 | 28 | func (c *Capstr) Dis(mem []byte, addr uint64) ([]models.Ins, error) { 29 | if c.cs == nil { 30 | if err := c.Open(); err != nil { 31 | return nil, err 32 | } 33 | } 34 | // FIXME: hack, thumb detection should be injected by the ARM arch 35 | // detect thumb 36 | if len(mem) == 2 && c.Arch == cs.ARCH_ARM && c.Mode == cs.MODE_ARM { 37 | if c.thumb == nil { 38 | c.thumb = &Capstr{Arch: cs.ARCH_ARM, Mode: cs.MODE_THUMB} 39 | } 40 | return c.thumb.Dis(mem, addr) 41 | } 42 | if ent := c.dc.Get(addr, mem); ent != nil { 43 | return ent.Dis, nil 44 | } 45 | dis, err := c.cs.Dis(mem, addr, 0) 46 | if err != nil { 47 | return nil, errors.Wrap(err, "capstone disassembly failed") 48 | } 49 | ret := make([]models.Ins, len(dis)) 50 | for i, v := range dis { 51 | ret[i] = v 52 | } 53 | c.dc.Put(addr, mem, ret) 54 | return ret, nil 55 | } 56 | -------------------------------------------------------------------------------- /go/cmd/com/com.go: -------------------------------------------------------------------------------- 1 | package com 2 | 3 | import ( 4 | "github.com/pkg/errors" 5 | "os" 6 | 7 | "github.com/lunixbochs/usercorn/go" 8 | "github.com/lunixbochs/usercorn/go/cmd" 9 | "github.com/lunixbochs/usercorn/go/loader" 10 | "github.com/lunixbochs/usercorn/go/models" 11 | ) 12 | 13 | func Main(args []string) { 14 | c := cmd.NewUsercornRawCmd() 15 | c.NoArgs = true 16 | 17 | c.MakeUsercorn = func(exe string) (models.Usercorn, error) { 18 | l, err := loader.NewComLoader(exe) 19 | if err != nil { 20 | return nil, errors.Wrap(err, "failed to load COM file") 21 | } 22 | u, err := usercorn.NewUsercornRaw(l, c.Config) 23 | 24 | // Map in entire 16 bit address space 25 | err = u.MemMap(0, 0x10000, 7) 26 | if err != nil { 27 | return nil, errors.Wrap(err, "failed to map in address space") 28 | } 29 | 30 | // Write in binary's data 31 | segments, err := l.Segments() 32 | if err != nil { 33 | return nil, errors.Wrap(err, "failed to get segments from loader") 34 | } 35 | for _, seg := range segments { 36 | data, err := seg.Data() 37 | if err != nil { 38 | return nil, errors.Wrap(err, "failed to read segment data") 39 | } 40 | 41 | err = u.MemWrite(seg.Addr, data) 42 | if err != nil { 43 | return nil, errors.Wrap(err, "failed to write segment data") 44 | } 45 | } 46 | 47 | return u, nil 48 | } 49 | c.Run(args, os.Environ()) 50 | } 51 | 52 | func init() { cmd.Register("com", "execute a DOS COM binary", Main) } 53 | -------------------------------------------------------------------------------- /go/loader/com.go: -------------------------------------------------------------------------------- 1 | package loader 2 | 3 | import ( 4 | "encoding/binary" 5 | "github.com/pkg/errors" 6 | "io" 7 | "os" 8 | 9 | "github.com/lunixbochs/usercorn/go/models" 10 | ) 11 | 12 | type ComLoader struct { 13 | LoaderBase 14 | Size int 15 | r io.ReaderAt 16 | } 17 | 18 | func (c *ComLoader) OS() string { 19 | return "DOS" 20 | } 21 | 22 | func NewComLoader(filename string) (models.Loader, error) { 23 | r, err := os.Open(filename) 24 | if err != nil { 25 | return nil, errors.Wrap(err, "failed to open file") 26 | } 27 | stat, err := r.Stat() 28 | if err != nil { 29 | return nil, errors.Wrap(err, "failed to stat file") 30 | } 31 | size := stat.Size() 32 | 33 | if size == 0 { 34 | return nil, errors.New("Cannot read from file") 35 | } 36 | return &ComLoader{ 37 | LoaderBase: LoaderBase{ 38 | arch: "x86_16", 39 | bits: 16, 40 | byteOrder: binary.LittleEndian, 41 | os: "dos", 42 | entry: 0x100, 43 | }, 44 | Size: int(size), 45 | r: r, 46 | }, nil 47 | } 48 | 49 | func (r *ComLoader) Segments() ([]models.SegmentData, error) { 50 | var segs []models.SegmentData 51 | 52 | // Main segment 53 | segs = append(segs, models.SegmentData{ 54 | Off: 0, 55 | Addr: 0x100, 56 | Size: uint64(r.Size), 57 | Prot: 7, 58 | DataFunc: func() ([]byte, error) { 59 | p := make([]byte, r.Size) 60 | n, err := r.r.ReadAt(p, 0) 61 | // Eat EOF error 62 | if err == io.EOF { 63 | err = nil 64 | } 65 | return p[:n], err 66 | }, 67 | }) 68 | return segs, nil 69 | } 70 | -------------------------------------------------------------------------------- /go/native/statfs.go: -------------------------------------------------------------------------------- 1 | package native 2 | 3 | import ( 4 | "syscall" 5 | ) 6 | 7 | type LinuxStatfs_t struct { 8 | Type int64 `struc:"off_t"` 9 | Bsize int64 `struc:"off_t"` 10 | Blocks uint64 `struc:"size_t"` 11 | Bfree uint64 `struc:"size_t"` 12 | Bavail uint64 `struc:"size_t"` 13 | Files uint64 `struc:"size_t"` 14 | Ffree uint64 `struc:"size_t"` 15 | Fsid syscall.Fsid 16 | Namelen int64 17 | // fragment size, whatever that is 18 | Frsize int64 `struc:"off_t"` 19 | // since linux 2.6 20 | Flags int64 `struc:"off_t"` 21 | Spare [5]int64 `struc:"off_t"` 22 | } 23 | 24 | type DarwinStatfs32_t struct { 25 | Otype int16 26 | Oflags int16 27 | Bsize int 28 | Iosize int 29 | Blocks int 30 | Bfree int 31 | Bavail int 32 | Files int 33 | Ffree int 34 | Fsid syscall.Fsid 35 | Owner int 36 | Reserved1 int16 37 | Type int16 38 | Flags int32 39 | Reserved2 int32 40 | Fstypename [13]byte 41 | Mntonname [90]byte 42 | Mntfromname [90]byte 43 | Reserved3 byte 44 | Reserved4 [4]int 45 | } 46 | 47 | type DarwinStatfs64_t struct { 48 | Bsize uint32 49 | Iosize int32 50 | Blocks uint64 51 | Bfree uint64 52 | Bavail uint64 53 | Files uint64 54 | Ffree uint64 55 | Fsid syscall.Fsid 56 | Owner int 57 | Type uint32 58 | Flags uint32 59 | Fssubtype uint32 60 | Fstypename [16]byte 61 | Mntonname [1024]byte 62 | Mntfromname [1024]byte 63 | Reserved [8]uint32 64 | } 65 | -------------------------------------------------------------------------------- /go/models/struc_stream.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "github.com/lunixbochs/struc" 5 | "github.com/pkg/errors" 6 | "io" 7 | ) 8 | 9 | type StrucStream struct { 10 | Stream io.ReadWriter 11 | Options *struc.Options 12 | Error error 13 | } 14 | 15 | func NewStrucStream(f io.ReadWriter, opts *struc.Options) *StrucStream { 16 | return &StrucStream{Stream: f, Options: opts} 17 | } 18 | 19 | func (s *StrucStream) Pack(vals ...interface{}) error { 20 | if s.Error != nil { 21 | return s.Error 22 | } 23 | for _, val := range vals { 24 | if s.Error = struc.PackWithOptions(s.Stream, val, s.Options); s.Error != nil { 25 | s.Error = errors.Wrap(s.Error, "struc.PackWithOptions() failed") 26 | return s.Error 27 | } 28 | } 29 | return nil 30 | } 31 | 32 | func (s *StrucStream) Unpack(vals ...interface{}) error { 33 | if s.Error != nil { 34 | return s.Error 35 | } 36 | for _, val := range vals { 37 | if s.Error = struc.UnpackWithOptions(s.Stream, val, s.Options); s.Error != nil { 38 | s.Error = errors.Wrap(s.Error, "struc.UnpackWithOptions() failed") 39 | return s.Error 40 | } 41 | } 42 | return nil 43 | } 44 | 45 | func (s *StrucStream) Sizeof(vals ...interface{}) (int, error) { 46 | if s.Error != nil { 47 | return 0, s.Error 48 | } 49 | var n, size int 50 | for _, val := range vals { 51 | n, s.Error = struc.SizeofWithOptions(val, s.Options) 52 | if s.Error != nil { 53 | s.Error = errors.Wrap(s.Error, "struc.SizeofWithOptions() failed") 54 | return 0, s.Error 55 | } 56 | size += n 57 | } 58 | return size, nil 59 | } 60 | -------------------------------------------------------------------------------- /go/kernel/common/syscall.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | type Syscall struct { 9 | Name string 10 | Kernel *KernelBase 11 | Instance reflect.Value 12 | Method reflect.Method 13 | In []reflect.Type 14 | Out []reflect.Type 15 | ObufArr bool 16 | UintArr bool 17 | } 18 | 19 | // Call a syscall from the dispatch table. Will panic() if anything goes terribly wrong. 20 | func (sys Syscall) Call(args []uint64) uint64 { 21 | extraArgs := 1 22 | if sys.ObufArr || sys.UintArr { 23 | extraArgs += 1 24 | } 25 | in := make([]reflect.Value, len(sys.In)+extraArgs) 26 | in[0] = sys.Instance 27 | // special case "all args" buf list 28 | if sys.ObufArr && len(sys.In) > 1 { 29 | arr := make([]Obuf, len(sys.In)-1) 30 | for i := range arr { 31 | arr[i] = Obuf{NewBuf(sys.Kernel, args[i])} 32 | } 33 | in[1] = reflect.ValueOf(arr) 34 | } else if sys.UintArr { 35 | in[1] = reflect.ValueOf(args) 36 | } 37 | // convert syscall arguments 38 | converted, err := sys.Kernel.Argjoy.Convert(sys.In, false, args) 39 | if err != nil { 40 | msg := fmt.Sprintf("calling %T.%s(): %s", sys.Instance.Interface(), sys.Method.Name, err) 41 | panic(msg) 42 | } 43 | copy(in[extraArgs:], converted) 44 | // call handler function 45 | out := sys.Method.Func.Call(in) 46 | // return output if first return of function is representable as an int type 47 | Uint64Type := reflect.TypeOf(uint64(0)) 48 | if len(out) > 0 && out[0].Type().ConvertibleTo(Uint64Type) { 49 | return out[0].Convert(Uint64Type).Uint() 50 | } 51 | return 0 52 | } 53 | -------------------------------------------------------------------------------- /go/loader/loader.go: -------------------------------------------------------------------------------- 1 | package loader 2 | 3 | import ( 4 | "debug/dwarf" 5 | "encoding/binary" 6 | 7 | "github.com/lunixbochs/usercorn/go/models" 8 | ) 9 | 10 | type LoaderBase struct { 11 | arch string 12 | bits int 13 | byteOrder binary.ByteOrder 14 | os string 15 | entry uint64 16 | symCache []models.Symbol 17 | } 18 | 19 | func (l *LoaderBase) Arch() string { 20 | return l.arch 21 | } 22 | 23 | func (l *LoaderBase) Bits() int { 24 | return l.bits 25 | } 26 | 27 | func (l *LoaderBase) ByteOrder() binary.ByteOrder { 28 | if l.byteOrder == nil { 29 | return binary.LittleEndian 30 | } 31 | return l.byteOrder 32 | } 33 | 34 | func (l *LoaderBase) OS() string { 35 | return l.os 36 | } 37 | 38 | func (l *LoaderBase) Entry() uint64 { 39 | return l.entry 40 | } 41 | 42 | // Everything below this line is a stub, intended to be reimplemented by the struct embedding LoaderBase. 43 | // These methods are defined to allow implementing a partial loader. 44 | 45 | func (l *LoaderBase) DWARF() (*dwarf.Data, error) { 46 | return nil, nil 47 | } 48 | 49 | func (l *LoaderBase) DataSegment() (uint64, uint64) { 50 | return 0, 0 51 | } 52 | 53 | func (l *LoaderBase) Header() (uint64, []byte, int) { 54 | return 0, nil, 0 55 | } 56 | 57 | func (l *LoaderBase) Interp() string { 58 | return "" 59 | } 60 | 61 | func (l *LoaderBase) Segments() ([]models.SegmentData, error) { 62 | return nil, nil 63 | } 64 | 65 | func (l *LoaderBase) Symbols() ([]models.Symbol, error) { 66 | return nil, nil 67 | } 68 | 69 | func (l *LoaderBase) Type() int { 70 | return EXEC 71 | } 72 | -------------------------------------------------------------------------------- /go/arch/x86/darwin.go: -------------------------------------------------------------------------------- 1 | package x86 2 | 3 | import ( 4 | "github.com/lunixbochs/ghostrace/ghost/sys/num" 5 | uc "github.com/unicorn-engine/unicorn/bindings/go/unicorn" 6 | 7 | "github.com/lunixbochs/usercorn/go/kernel/common" 8 | "github.com/lunixbochs/usercorn/go/kernel/darwin" 9 | "github.com/lunixbochs/usercorn/go/models" 10 | ) 11 | 12 | const ( 13 | dwMach = 1 14 | dwUnix = 2 15 | dwMdep = 3 16 | dwDiag = 4 17 | ) 18 | 19 | func DarwinKernels(u models.Usercorn) []interface{} { 20 | return []interface{}{darwin.NewKernel(u)} 21 | } 22 | 23 | func DarwinInit(u models.Usercorn, args, env []string) error { 24 | if err := darwin.StackInit(u, args, env); err != nil { 25 | return err 26 | } 27 | // FIXME: lib43 crashes if 32-bit darwin gets mach header. maybe I need to align the stack. 28 | u.Pop() 29 | return nil 30 | } 31 | 32 | func DarwinSyscall(u models.Usercorn, class int) { 33 | getArgs := common.StackArgs(u) 34 | 35 | eax, _ := u.RegRead(uc.X86_REG_EAX) 36 | nr := class<<24 | int(eax) 37 | name, _ := num.Darwin_x86_mach[nr] 38 | 39 | ret, _ := u.Syscall(nr, name, getArgs) 40 | u.RegWrite(uc.X86_REG_EAX, ret) 41 | } 42 | 43 | func DarwinInterrupt(u models.Usercorn, intno uint32) { 44 | switch intno { 45 | case 0x80: 46 | DarwinSyscall(u, dwUnix) 47 | case 0x81: 48 | DarwinSyscall(u, dwMach) 49 | case 0x82: 50 | DarwinSyscall(u, dwMdep) 51 | case 0x83: 52 | DarwinSyscall(u, dwDiag) 53 | } 54 | } 55 | 56 | func init() { 57 | Arch.RegisterOS(&models.OS{ 58 | Name: "darwin", 59 | Kernels: DarwinKernels, 60 | Init: DarwinInit, 61 | Interrupt: DarwinInterrupt, 62 | }) 63 | } 64 | -------------------------------------------------------------------------------- /go/arch/arm64/linux.go: -------------------------------------------------------------------------------- 1 | package arm64 2 | 3 | import ( 4 | "fmt" 5 | sysnum "github.com/lunixbochs/ghostrace/ghost/sys/num" 6 | uc "github.com/unicorn-engine/unicorn/bindings/go/unicorn" 7 | 8 | "github.com/lunixbochs/usercorn/go/kernel/common" 9 | "github.com/lunixbochs/usercorn/go/kernel/linux" 10 | "github.com/lunixbochs/usercorn/go/models" 11 | ) 12 | 13 | var LinuxRegs = []int{uc.ARM64_REG_X0, uc.ARM64_REG_X1, uc.ARM64_REG_X2, uc.ARM64_REG_X3, uc.ARM64_REG_X4, uc.ARM64_REG_X5} 14 | 15 | type Arm64LinuxKernel struct { 16 | *linux.LinuxKernel 17 | tls uint64 18 | } 19 | 20 | func (k *Arm64LinuxKernel) SetTls(addr uint64) { 21 | k.tls = addr 22 | } 23 | 24 | func LinuxKernels(u models.Usercorn) []interface{} { 25 | kernel := &Arm64LinuxKernel{LinuxKernel: linux.NewKernel()} 26 | return []interface{}{kernel} 27 | } 28 | 29 | func LinuxInit(u models.Usercorn, args, env []string) error { 30 | if err := EnableFPU(u); err != nil { 31 | return err 32 | } 33 | return linux.StackInit(u, args, env) 34 | } 35 | 36 | func LinuxSyscall(u models.Usercorn, num int) { 37 | name, _ := sysnum.Linux_arm64[int(num)] 38 | ret, _ := u.Syscall(int(num), name, common.RegArgs(u, LinuxRegs)) 39 | u.RegWrite(uc.ARM64_REG_X0, ret) 40 | } 41 | 42 | func LinuxInterrupt(u models.Usercorn, intno uint32) { 43 | if intno == 2 { 44 | num, _ := u.RegRead(uc.ARM64_REG_X8) 45 | LinuxSyscall(u, int(num)) 46 | return 47 | } 48 | panic(fmt.Sprintf("unhandled ARM interrupt: %d", intno)) 49 | } 50 | 51 | func init() { 52 | Arch.RegisterOS(&models.OS{ 53 | Name: "linux", 54 | Kernels: LinuxKernels, 55 | Init: LinuxInit, 56 | Interrupt: LinuxInterrupt, 57 | }) 58 | } 59 | -------------------------------------------------------------------------------- /go/kernel/posix/process.go: -------------------------------------------------------------------------------- 1 | package posix 2 | 3 | import ( 4 | "os" 5 | "syscall" 6 | 7 | co "github.com/lunixbochs/usercorn/go/kernel/common" 8 | "github.com/lunixbochs/usercorn/go/models" 9 | ) 10 | 11 | func (k *PosixKernel) Exit(code int) { 12 | k.U.Exit(models.ExitStatus(code)) 13 | } 14 | 15 | func (k *PosixKernel) ExitGroup(code int) { 16 | k.Exit(code) 17 | } 18 | 19 | func (k *PosixKernel) Getpid() int { 20 | return os.Getpid() 21 | } 22 | 23 | func (k *PosixKernel) Getppid() int { 24 | return os.Getppid() 25 | } 26 | 27 | func (k *PosixKernel) Getpgid(pid int) uint64 { 28 | n, err := syscall.Getpgid(pid) 29 | if err != nil { 30 | return Errno(err) 31 | } 32 | return uint64(n) 33 | } 34 | 35 | func (k *PosixKernel) Getpgrp() int { 36 | return syscall.Getpgrp() 37 | } 38 | 39 | func (k *PosixKernel) Kill(pid, signal int) uint64 { 40 | // TODO: os-specific signal handling? 41 | return Errno(syscall.Kill(pid, syscall.Signal(signal))) 42 | } 43 | 44 | func (k *PosixKernel) Execve(path string, argvBuf, envpBuf co.Buf) uint64 { 45 | // TODO: put this function somewhere generic? 46 | readStrArray := func(buf co.Buf) []string { 47 | var out []string 48 | st := buf.Struc() 49 | for { 50 | var addr uint64 51 | if k.U.Bits() == 64 { 52 | st.Unpack(&addr) 53 | } else { 54 | var addr32 uint32 55 | st.Unpack(&addr32) 56 | addr = uint64(addr32) 57 | } 58 | if addr == 0 { 59 | break 60 | } 61 | s, _ := k.U.Mem().ReadStrAt(addr) 62 | out = append(out, s) 63 | } 64 | return out 65 | } 66 | argv := readStrArray(argvBuf) 67 | envp := readStrArray(envpBuf) 68 | return Errno(syscall.Exec(path, argv, envp)) 69 | } 70 | -------------------------------------------------------------------------------- /go/models/cpu/enums.go: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import ( 4 | uc "github.com/unicorn-engine/unicorn/bindings/go/unicorn" 5 | ) 6 | 7 | // base hook enums on Unicorn's for simplicity 8 | const ( 9 | // hook CPU interrupts 10 | HOOK_INTR = uc.HOOK_INTR 11 | 12 | // hook one instruction (cpu-specific) 13 | HOOK_INSN = uc.HOOK_INSN 14 | 15 | // hook each executed instruction 16 | HOOK_CODE = uc.HOOK_CODE 17 | 18 | // hook each executed basic block 19 | HOOK_BLOCK = uc.HOOK_BLOCK 20 | 21 | // hook (before) each memory read/write 22 | HOOK_MEM_FETCH = uc.HOOK_MEM_FETCH 23 | HOOK_MEM_READ = uc.HOOK_MEM_READ 24 | HOOK_MEM_WRITE = uc.HOOK_MEM_WRITE 25 | // HOOK_MEM_READ_AFTER = uc.HOOK_MEM_READ_AFTER 26 | 27 | // hook all memory errors 28 | HOOK_MEM_ERR = uc.HOOK_MEM_INVALID 29 | ) 30 | 31 | // these errors are used for HOOK_MEM_ERR 32 | const ( 33 | MEM_WRITE_UNMAPPED = uc.MEM_WRITE_UNMAPPED 34 | MEM_READ_UNMAPPED = uc.MEM_READ_UNMAPPED 35 | MEM_FETCH_UNMAPPED = uc.MEM_FETCH_UNMAPPED 36 | MEM_WRITE_PROT = uc.MEM_WRITE_PROT 37 | MEM_READ_PROT = uc.MEM_READ_PROT 38 | MEM_FETCH_PROT = uc.MEM_FETCH_PROT 39 | 40 | MEM_PROT = MEM_WRITE_PROT | MEM_READ_PROT | MEM_FETCH_PROT 41 | MEM_UNMAPPED = MEM_READ_UNMAPPED | MEM_WRITE_UNMAPPED | MEM_FETCH_UNMAPPED 42 | ) 43 | 44 | // these constants are used for memory protections 45 | const ( 46 | PROT_NONE = uc.PROT_NONE 47 | PROT_READ = uc.PROT_READ 48 | PROT_WRITE = uc.PROT_WRITE 49 | PROT_EXEC = uc.PROT_EXEC 50 | PROT_ALL = uc.PROT_ALL 51 | ) 52 | 53 | // these constants are used in a hook to specify the type of memory access 54 | const ( 55 | MEM_WRITE = uc.MEM_WRITE 56 | MEM_READ = uc.MEM_READ 57 | MEM_FETCH = uc.MEM_FETCH 58 | ) 59 | -------------------------------------------------------------------------------- /go/arch/mips/linux.go: -------------------------------------------------------------------------------- 1 | package mips 2 | 3 | import ( 4 | "fmt" 5 | sysnum "github.com/lunixbochs/ghostrace/ghost/sys/num" 6 | uc "github.com/unicorn-engine/unicorn/bindings/go/unicorn" 7 | 8 | "github.com/lunixbochs/usercorn/go/kernel/common" 9 | "github.com/lunixbochs/usercorn/go/kernel/linux" 10 | "github.com/lunixbochs/usercorn/go/models" 11 | ) 12 | 13 | var LinuxRegs = []int{uc.MIPS_REG_A0, uc.MIPS_REG_A1, uc.MIPS_REG_A2, uc.MIPS_REG_A3, 0, 0} 14 | 15 | type MipsLinuxKernel struct { 16 | *linux.LinuxKernel 17 | } 18 | 19 | func (k *MipsLinuxKernel) SetThreadArea(addr uint64) error { 20 | // TODO: Unicorn needs CP0 register support 21 | return k.U.RunAsm(0, "mtc0 $t0, $29", map[int]uint64{uc.MIPS_REG_T0: addr}, nil) 22 | } 23 | 24 | func LinuxKernels(u models.Usercorn) []interface{} { 25 | kernel := &MipsLinuxKernel{LinuxKernel: linux.NewKernel()} 26 | return []interface{}{kernel} 27 | } 28 | 29 | func LinuxInit(u models.Usercorn, args, env []string) error { 30 | return linux.StackInit(u, args, env) 31 | } 32 | 33 | func LinuxSyscall(u models.Usercorn) { 34 | // TODO: handle errors or something 35 | num, _ := u.RegRead(uc.MIPS_REG_V0) 36 | name, _ := sysnum.Linux_mips[int(num)] 37 | ret, _ := u.Syscall(int(num), name, common.RegArgs(u, LinuxRegs)) 38 | u.RegWrite(uc.MIPS_REG_V0, ret) 39 | } 40 | 41 | func LinuxInterrupt(u models.Usercorn, cause uint32) { 42 | intno := (cause >> 1) & 15 43 | if intno == 8 { 44 | LinuxSyscall(u) 45 | return 46 | } 47 | panic(fmt.Sprintf("unhandled MIPS interrupt %d", intno)) 48 | } 49 | 50 | func init() { 51 | Arch.RegisterOS(&models.OS{ 52 | Name: "linux", 53 | Kernels: LinuxKernels, 54 | Init: LinuxInit, 55 | Interrupt: LinuxInterrupt, 56 | }) 57 | } 58 | -------------------------------------------------------------------------------- /go/kernel/darwin/kernel.go: -------------------------------------------------------------------------------- 1 | package darwin 2 | 3 | import ( 4 | "crypto/rand" 5 | "fmt" 6 | 7 | co "github.com/lunixbochs/usercorn/go/kernel/common" 8 | "github.com/lunixbochs/usercorn/go/kernel/mach" 9 | "github.com/lunixbochs/usercorn/go/kernel/posix" 10 | "github.com/lunixbochs/usercorn/go/models" 11 | ) 12 | 13 | const ( 14 | STACK_BASE = 0x60000000 15 | STACK_SIZE = 0x00800000 16 | ) 17 | 18 | type DarwinKernel struct { 19 | *co.KernelBase 20 | mach.MachKernel 21 | posix.PosixKernel 22 | } 23 | 24 | func NewKernel(u models.Usercorn) *DarwinKernel { 25 | kernel := &DarwinKernel{ 26 | KernelBase: &co.KernelBase{}, 27 | MachKernel: *mach.NewKernel(), 28 | PosixKernel: *posix.NewKernel(), 29 | } 30 | kernel.MachKernel.KernelBase = kernel.KernelBase 31 | kernel.PosixKernel.KernelBase = kernel.KernelBase 32 | kernel.U = u 33 | registerUnpack(kernel) 34 | return kernel 35 | } 36 | 37 | func StackInit(u models.Usercorn, args, env []string) error { 38 | if err := u.MapStack(STACK_BASE, STACK_SIZE, false); err != nil { 39 | return err 40 | } 41 | var tmp [8]byte 42 | rand.Read(tmp[:]) 43 | stackGuard := u.UnpackAddr(tmp[:]) 44 | 45 | auxvStrings := []string{ 46 | u.Exe(), 47 | fmt.Sprintf("stack_guard=0x%x", stackGuard), 48 | } 49 | addrs, err := posix.PushStrings(u, auxvStrings...) 50 | if err != nil { 51 | return err 52 | } 53 | auxv, err := posix.PackAddrs(u, addrs) 54 | if err != nil { 55 | return err 56 | } 57 | err = posix.StackInit(u, args, env, auxv) 58 | if err != nil { 59 | return err 60 | } 61 | // offset to mach_header at exe[0:] in guest memory 62 | textOffset, _, _ := u.Loader().Header() 63 | offset := u.Base() + textOffset 64 | _, err = u.Push(offset) 65 | return err 66 | } 67 | -------------------------------------------------------------------------------- /go/kernel/posix/time.go: -------------------------------------------------------------------------------- 1 | package posix 2 | 3 | import ( 4 | "github.com/lunixbochs/struc" 5 | "syscall" 6 | "time" 7 | 8 | co "github.com/lunixbochs/usercorn/go/kernel/common" 9 | "github.com/lunixbochs/usercorn/go/native" 10 | ) 11 | 12 | func (k *PosixKernel) Time(out co.Obuf) uint64 { 13 | t := time.Now().Unix() 14 | if out.Addr != 0 { 15 | out.Pack(struc.Size_t(t)) 16 | } 17 | return uint64(t) 18 | } 19 | 20 | func (k *PosixKernel) ClockGettime(_ int, out co.Obuf) uint64 { 21 | ts := syscall.NsecToTimespec(time.Now().UnixNano()) 22 | err := out.Pack(&native.Timespec{Sec: int64(ts.Sec), Nsec: int64(ts.Nsec)}) 23 | if err != nil { 24 | return UINT64_MAX // FIXME 25 | } 26 | return 0 27 | } 28 | 29 | func (k *PosixKernel) Nanosleep(req *native.Timespec, rem co.Obuf) uint64 { 30 | time.Sleep(req.Duration()) 31 | // TODO: 1. allow interrupts 32 | // TODO: 2. handle remaining time 33 | if err := rem.Pack(&native.Timespec{}); err != nil { 34 | return UINT64_MAX // FIXME 35 | } 36 | return 0 37 | } 38 | 39 | // TODO: candidate for enum conversion 40 | // but OS X and Linux appeared to have the same values for now 41 | // TODO: these are stubbed because they're mostly for progress bars afaik 42 | func (k *PosixKernel) Setitimer(which int, value co.Obuf) uint64 { 43 | return 0 44 | } 45 | 46 | func (k *PosixKernel) Getitimer(which int, value *native.Itimerval, ovalue co.Obuf) uint64 { 47 | return 0 48 | } 49 | 50 | func (k *PosixKernel) ClockGetres(clockid int, out co.Obuf) uint64 { 51 | // TODO: I'm just assuming you have a nanosecond-accurate clock available 52 | if out.Addr != 0 { 53 | res := native.Timespec{ 54 | Sec: 0, 55 | Nsec: 1, 56 | } 57 | if err := out.Pack(&res); err != nil { 58 | return UINT64_MAX // FIXME 59 | } 60 | } 61 | return 0 62 | } 63 | -------------------------------------------------------------------------------- /go/cmd/cfg/cfg.go: -------------------------------------------------------------------------------- 1 | package cfg 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/lunixbochs/usercorn/go/models" 7 | "github.com/lunixbochs/usercorn/go/models/cpu" 8 | ) 9 | 10 | type Block struct { 11 | u models.Usercorn 12 | 13 | Addr, Size uint64 14 | } 15 | 16 | func (b *Block) Print() { 17 | u := b.u 18 | u.Printf("[Block 0x%x-0x%x]\n", b.Addr, b.Addr+b.Size) 19 | dis, _ := u.Dis(b.Addr, b.Size, false) 20 | u.Printf("%s", dis) 21 | u.Println("") 22 | } 23 | 24 | type BacktrackEngine struct { 25 | u models.Usercorn 26 | } 27 | 28 | func (b *BacktrackEngine) Run() {} 29 | 30 | func EmuCfg(u models.Usercorn, backtrack bool) map[uint64]*Block { 31 | u.Config().BlockSyscalls = true 32 | 33 | blocks := make(map[uint64]*Block) 34 | u.HookAdd(cpu.HOOK_BLOCK, func(_ cpu.Cpu, addr uint64, size uint32) { 35 | blocks[addr] = &Block{u, addr, uint64(size)} 36 | }, 1, 0) 37 | 38 | // TODO: instead of the below... support read-only filesystem operations, just nothing destructive? 39 | // like open, read, mmap 40 | // otherwise we won't be able to map a binary 41 | 42 | // automatically map unmapped pages (besides the zero page) 43 | u.HookAdd(cpu.HOOK_MEM_ERR, func(_ cpu.Cpu, errno int, addr uint64, size int, value int64) bool { 44 | if errno == cpu.MEM_UNMAPPED && addr > 0x1000 { 45 | align := addr & ^uint64(0xfff) 46 | u.MemMap(align, align+0x1000, cpu.PROT_ALL) 47 | return true 48 | } 49 | return false 50 | }, 1, 0) 51 | 52 | if backtrack { 53 | bt := &BacktrackEngine{u} 54 | bt.Run() 55 | } else { 56 | u.Gate().UnlockStopRelock() 57 | } 58 | return blocks 59 | } 60 | 61 | func StaticCfg() { 62 | } 63 | 64 | func CfgMain(u models.Usercorn, backtrack, json bool) error { 65 | blocks := EmuCfg(u, backtrack) 66 | fmt.Println("here", len(blocks)) 67 | return nil 68 | } 69 | -------------------------------------------------------------------------------- /go/kernel/linux/unpack.go: -------------------------------------------------------------------------------- 1 | package linux 2 | 3 | import ( 4 | "github.com/lunixbochs/argjoy" 5 | "syscall" 6 | 7 | co "github.com/lunixbochs/usercorn/go/kernel/common" 8 | "github.com/lunixbochs/usercorn/go/kernel/linux/unpack" 9 | "github.com/lunixbochs/usercorn/go/native" 10 | "github.com/lunixbochs/usercorn/go/native/enum" 11 | ) 12 | 13 | func Unpack(k *LinuxKernel, arg interface{}, vals []interface{}) error { 14 | reg0 := vals[0].(uint64) 15 | // null pointer guard 16 | if reg0 == 0 { 17 | // work around syscall package panicking on null Sockaddr 18 | switch v := arg.(type) { 19 | case *syscall.Sockaddr: 20 | *v = &syscall.SockaddrInet4{} 21 | } 22 | return nil 23 | } 24 | buf := co.NewBuf(k, reg0) 25 | switch v := arg.(type) { 26 | case *syscall.Sockaddr: 27 | *v = unpack.Sockaddr(buf, int(vals[1].(uint64))) 28 | case **syscall.Timeval: 29 | tmp := &native.Timeval{} 30 | if err := buf.Unpack(tmp); err != nil { 31 | return err 32 | } 33 | nsec := tmp.Sec*1e9 + tmp.Usec*1e3 34 | *v = &syscall.Timeval{} 35 | **v = syscall.NsecToTimeval(nsec) 36 | case **native.Fdset32: 37 | tmp := &native.Fdset32{} 38 | if err := buf.Unpack(tmp); err != nil { 39 | return err 40 | } 41 | *v = tmp 42 | case **native.Timespec: 43 | tmp := &native.Timespec{} 44 | if err := buf.Unpack(tmp); err != nil { 45 | return err 46 | } 47 | *v = tmp 48 | case *enum.OpenFlag: 49 | *v = unpack.OpenFlag(reg0) 50 | case *enum.MmapFlag: 51 | *v = unpack.MmapFlag(reg0) 52 | case *enum.MmapProt: 53 | *v = unpack.MmapProt(reg0) 54 | default: 55 | return argjoy.NoMatch 56 | } 57 | return nil 58 | } 59 | 60 | func registerUnpack(k *LinuxKernel) { 61 | k.Argjoy.Register(func(arg interface{}, vals []interface{}) error { 62 | return Unpack(k, arg, vals) 63 | }) 64 | } 65 | -------------------------------------------------------------------------------- /go/cmd/cgc/io.go: -------------------------------------------------------------------------------- 1 | package cgc 2 | 3 | import ( 4 | "bytes" 5 | "encoding/hex" 6 | "io" 7 | "log" 8 | "sync" 9 | ) 10 | 11 | type NullIO struct{} 12 | 13 | func (n *NullIO) Read(p []byte) (int, error) { return 0, io.EOF } 14 | func (n *NullIO) Write(p []byte) (int, error) { return 0, nil } 15 | 16 | type BufPipe struct { 17 | b bytes.Buffer 18 | c sync.Cond 19 | l sync.Mutex 20 | } 21 | 22 | func NewBufPipe() *BufPipe { 23 | b := &BufPipe{} 24 | b.c = sync.Cond{L: &b.l} 25 | return b 26 | } 27 | 28 | func NewBufPipePair() (io.ReadWriter, io.ReadWriter) { 29 | a, b := NewBufPipe(), NewBufPipe() 30 | return ReadWriter{a, b}, ReadWriter{b, a} 31 | } 32 | 33 | func (b *BufPipe) Read(p []byte) (int, error) { 34 | b.c.L.Lock() 35 | defer b.c.L.Unlock() 36 | // block if empty 37 | for b.b.Len() == 0 { 38 | b.c.Wait() 39 | } 40 | return b.b.Read(p) 41 | } 42 | 43 | func (b *BufPipe) Write(p []byte) (int, error) { 44 | b.c.L.Lock() 45 | defer b.c.L.Unlock() 46 | defer b.c.Signal() 47 | return b.b.Write(p) 48 | } 49 | 50 | type ReadWriter struct { 51 | io.Reader 52 | io.Writer 53 | } 54 | 55 | type WriteLogger struct { 56 | Prefix string 57 | Hex bool 58 | buf bytes.Buffer 59 | } 60 | 61 | func (w *WriteLogger) Read(p []byte) (int, error) { 62 | return 0, io.EOF 63 | } 64 | 65 | func (w *WriteLogger) Write(p []byte) (int, error) { 66 | w.buf.Write(p) 67 | if bytes.Contains(w.buf.Bytes(), []byte("\n")) { 68 | lines := bytes.Split(w.buf.Bytes(), []byte("\n")) 69 | last := len(lines) - 1 70 | for _, line := range lines[:last] { 71 | s := string(line) 72 | if w.Hex { 73 | s = hex.EncodeToString(line) 74 | } 75 | log.Printf("%s: %s", w.Prefix, s) 76 | } 77 | w.buf.Reset() 78 | w.buf.Write(lines[last]) 79 | } 80 | return 0, nil 81 | } 82 | 83 | func (w *WriteLogger) Close() error { return nil } 84 | -------------------------------------------------------------------------------- /go/models/context.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "fmt" 5 | "github.com/pkg/errors" 6 | "strings" 7 | 8 | "github.com/lunixbochs/usercorn/go/models/cpu" 9 | ) 10 | 11 | // like unicorn.ContextSave/Restore, but with memory mappings too 12 | 13 | type ContextMem struct { 14 | Addr, Size uint64 15 | Prot int 16 | Data []byte 17 | Desc string 18 | File *cpu.FileDesc 19 | } 20 | 21 | type Context struct { 22 | mem []*ContextMem 23 | ctxHandle interface{} 24 | } 25 | 26 | func ContextSave(u Usercorn) (*Context, error) { 27 | var err error 28 | ctx := &Context{} 29 | 30 | // save regs/cpu state 31 | ctx.ctxHandle, err = u.ContextSave(nil) 32 | if err != nil { 33 | return nil, err 34 | } 35 | 36 | // save memory mappings 37 | var errs []string 38 | for _, m := range u.Mappings() { 39 | mem, err := u.MemRead(m.Addr, m.Size) 40 | if err != nil { 41 | errs = append(errs, fmt.Sprintf("(%s) saving 0x%x-0x%x\n", err, m.Addr, m.Addr+m.Size)) 42 | continue 43 | } 44 | ctx.mem = append(ctx.mem, &ContextMem{ 45 | Addr: m.Addr, Size: m.Size, Prot: m.Prot, Data: mem, 46 | Desc: m.Desc, File: m.File, 47 | }) 48 | } 49 | if len(errs) > 0 { 50 | err = errors.New(strings.Join(errs, ", ")) 51 | } 52 | return ctx, err 53 | } 54 | 55 | func ContextRestore(u Usercorn, ctx *Context) error { 56 | // restore regs/cpu state 57 | if err := u.ContextRestore(ctx.ctxHandle); err != nil { 58 | return err 59 | } 60 | // unmap all memory 61 | for _, m := range u.Mappings() { 62 | u.MemUnmap(m.Addr, m.Size) 63 | } 64 | // restore saved memory 65 | // TODO: use mmap(fixed) for this 66 | for _, m := range ctx.mem { 67 | u.MemMap(m.Addr, m.Size, m.Prot) 68 | u.MemWrite(m.Addr, m.Data) 69 | // TODO: this could have a bug if the saved mapping overlapped with an existing mapping somehow 70 | x := u.Mappings() 71 | x[len(x)-1].Desc = m.Desc 72 | x[len(x)-1].File = m.File 73 | } 74 | return nil 75 | } 76 | -------------------------------------------------------------------------------- /go/arch/mips/arch.go: -------------------------------------------------------------------------------- 1 | package mips 2 | 3 | import ( 4 | ks "github.com/keystone-engine/keystone/bindings/go/keystone" 5 | cs "github.com/lunixbochs/capstr" 6 | uc "github.com/unicorn-engine/unicorn/bindings/go/unicorn" 7 | 8 | "github.com/lunixbochs/usercorn/go/cpu" 9 | "github.com/lunixbochs/usercorn/go/cpu/unicorn" 10 | "github.com/lunixbochs/usercorn/go/models" 11 | ) 12 | 13 | var Arch = &models.Arch{ 14 | Name: "mips", 15 | Bits: 32, 16 | Radare: "mips", 17 | 18 | Cpu: &unicorn.Builder{Arch: uc.ARCH_MIPS, Mode: uc.MODE_MIPS32 + uc.MODE_LITTLE_ENDIAN}, 19 | Dis: &cpu.Capstr{Arch: cs.ARCH_MIPS, Mode: cs.MODE_MIPS32 + cs.MODE_LITTLE_ENDIAN}, 20 | Asm: &cpu.Keystone{Arch: ks.ARCH_MIPS, Mode: ks.MODE_MIPS32 + ks.MODE_LITTLE_ENDIAN}, 21 | 22 | PC: uc.MIPS_REG_PC, 23 | SP: uc.MIPS_REG_SP, 24 | Regs: map[string]int{ 25 | "at": uc.MIPS_REG_AT, 26 | "v0": uc.MIPS_REG_V0, 27 | "v1": uc.MIPS_REG_V1, 28 | "a0": uc.MIPS_REG_A0, 29 | "a1": uc.MIPS_REG_A1, 30 | "a2": uc.MIPS_REG_A2, 31 | "a3": uc.MIPS_REG_A3, 32 | "t0": uc.MIPS_REG_T0, 33 | "t1": uc.MIPS_REG_T1, 34 | "t2": uc.MIPS_REG_T2, 35 | "t3": uc.MIPS_REG_T3, 36 | "t4": uc.MIPS_REG_T4, 37 | "t5": uc.MIPS_REG_T5, 38 | "t6": uc.MIPS_REG_T6, 39 | "t7": uc.MIPS_REG_T7, 40 | "t8": uc.MIPS_REG_T8, 41 | "t9": uc.MIPS_REG_T9, 42 | "s0": uc.MIPS_REG_S0, 43 | "s1": uc.MIPS_REG_S1, 44 | "s2": uc.MIPS_REG_S2, 45 | "s3": uc.MIPS_REG_S3, 46 | "s4": uc.MIPS_REG_S4, 47 | "s5": uc.MIPS_REG_S5, 48 | "s6": uc.MIPS_REG_S6, 49 | "s7": uc.MIPS_REG_S7, 50 | "s8": uc.MIPS_REG_S8, 51 | "k0": uc.MIPS_REG_K0, 52 | "k1": uc.MIPS_REG_K1, 53 | "gp": uc.MIPS_REG_GP, 54 | "sp": uc.MIPS_REG_SP, 55 | "ra": uc.MIPS_REG_RA, 56 | }, 57 | DefaultRegs: []string{ 58 | "at", 59 | "v0", "v1", 60 | "a0", "a1", "a2", "a3", 61 | "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9", 62 | "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", 63 | "k0", "k1", 64 | "gp", 65 | }, 66 | } 67 | -------------------------------------------------------------------------------- /go/models/loopdetect_test.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func assert(t *testing.T, flag bool, msg string) { 9 | if flag { 10 | t.Fatal(msg) 11 | } 12 | } 13 | 14 | func TestLoop(t *testing.T) { 15 | loop := NewLoop(3) 16 | for i := uint64(1); i <= 3; i++ { 17 | loop.Push(i) 18 | } 19 | msg := "loop.Next() inconsistent" 20 | for i := uint64(1); i <= 3; i++ { 21 | assert(t, loop.Next() != i, msg) 22 | } 23 | for i := uint64(1); i <= 3; i++ { 24 | assert(t, loop.Next() != i, msg) 25 | } 26 | } 27 | 28 | // TODO: negative test case 29 | // TODO: offset test case 30 | 31 | func TestLoopDetect(t *testing.T) { 32 | // test detecting n-length chain 33 | early := "loop detection triggered early" 34 | failed := "loop detection failed" 35 | for offset := 0; offset < 10; offset++ { 36 | for n := uint64(1); n <= 6; n++ { 37 | // test one loop 38 | detect := NewLoopDetect(int(n * 5)) 39 | for i := 0; i < offset-1; i++ { 40 | detect.Update(uint64(999 - i)) 41 | } 42 | if offset > 0 { 43 | detect.Update(0) 44 | } 45 | for i := uint64(1); i <= n; i++ { 46 | looped, _, count := detect.Update(i) 47 | assert(t, looped || count > 0, early) 48 | } 49 | for i := uint64(1); i < n; i++ { 50 | looped, _, count := detect.Update(i) 51 | assert(t, looped || count > 0, early) 52 | } 53 | looped, _, count := detect.Update(n) 54 | assert(t, !(looped && count == 1), failed) 55 | // test multiple loops 56 | for loops := 2; loops <= 5; loops++ { 57 | for i := uint64(1); i < n; i++ { 58 | looped, _, count := detect.Update(i) 59 | if !(looped && count == loops) { 60 | t.Fatal(fmt.Sprintf("recursive loop failed: %d, %d, %#v", count, loops, detect.History)) 61 | } 62 | } 63 | looped, _, count := detect.Update(n) 64 | assert(t, !(looped && count == loops), fmt.Sprintf("recursive loop wrap failed: %d, %d, %v", count, loops, detect.History)) 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /go/kernel/linux/thread.go: -------------------------------------------------------------------------------- 1 | package linux 2 | 3 | import ( 4 | "fmt" 5 | 6 | co "github.com/lunixbochs/usercorn/go/kernel/common" 7 | ) 8 | 9 | const ( 10 | FUTEX_WAIT = 0 11 | FUTEX_WAKE = 1 12 | FUTEX_FD = 2 13 | FUTEX_REQUEUE = 3 14 | FUTEX_CMP_REQUEUE = 4 15 | FUTEX_WAKE_OP = 5 16 | FUTEX_LOCK_PI = 6 17 | FUTEX_UNLOCK_PI = 7 18 | FUTEX_TRYLOCK_PI = 8 19 | FUTEX_WAIT_BITSET = 9 20 | FUTEX_WAKE_BITSET = 10 21 | FUTEX_WAIT_REQUEUE_PI = 11 22 | FUTEX_CMP_REQUEUE_PI = 12 23 | ) 24 | 25 | const ( 26 | FUTEX_PRIVATE_FLAG = 128 27 | FUTEX_CLOCK_REALTIME = 256 28 | FUTEX_CMD_MASK = ^(FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME) 29 | ) 30 | 31 | var ENOSYS = 33 32 | 33 | // timeout is a co.Buf here because some forms of futex don't pass it 34 | func (k *LinuxKernel) Futex(uaddr co.Buf, op, val int, timeout, uaddr2 co.Buf, val3 uint64) int { 35 | if op&FUTEX_CLOCK_REALTIME != 0 { 36 | return -ENOSYS 37 | } 38 | switch op & FUTEX_CMD_MASK { 39 | case FUTEX_WAIT: 40 | case FUTEX_WAKE: 41 | case FUTEX_WAIT_BITSET: 42 | case FUTEX_WAKE_BITSET: 43 | default: 44 | return -1 45 | } 46 | return 0 47 | } 48 | 49 | func (k *LinuxKernel) SetTidAddress(tidptr co.Buf) uint64 { 50 | return 0 51 | } 52 | 53 | func (k *LinuxKernel) Tgkill(tgid int, tid int, sig int) uint64 { 54 | return 0 55 | } 56 | 57 | /* 58 | long get_robust_list(int pid, struct robust_list_head **head_ptr, 59 | size_t *len_ptr); 60 | long set_robust_list(struct robust_list_head *head, size_t len); 61 | */ 62 | 63 | func (k *LinuxKernel) SetRobustList(tid int, head co.Buf) {} 64 | func (k *LinuxKernel) GetRobustList(tid int, head co.Buf, size co.Off) {} 65 | 66 | func (k *LinuxKernel) Clone(flags uint64, stack, ptid, ctid, regs co.Buf) { 67 | var uregs [18]uint32 68 | regs.Unpack(&uregs) 69 | for i, v := range uregs { 70 | fmt.Printf("reg[%d] %#x\n", i, v) 71 | } 72 | panic("clone unimplemented") 73 | } 74 | -------------------------------------------------------------------------------- /go/kernel/linux/unpack/sockaddr.go: -------------------------------------------------------------------------------- 1 | package unpack 2 | 3 | import ( 4 | "encoding/binary" 5 | "syscall" 6 | 7 | "github.com/lunixbochs/usercorn/go/kernel/common" 8 | ) 9 | 10 | const ( 11 | AF_LOCAL = 1 12 | AF_INET = 2 13 | AF_INET6 = 10 14 | AF_PACKET = 17 15 | AF_NETLINK = 16 16 | ) 17 | 18 | type SockaddrInet4 struct { 19 | Family uint16 20 | Port uint16 21 | Addr [4]byte 22 | } 23 | 24 | type SockaddrInet6 struct { 25 | Family uint16 26 | Port uint16 27 | Flowinfo uint32 28 | Addr [16]byte 29 | Scope_id uint32 30 | } 31 | 32 | type SockaddrLinklayer struct { 33 | Family uint16 34 | Protocol uint16 35 | Ifindex int32 36 | Hatype uint16 37 | Pkttype uint8 38 | Halen uint8 39 | Addr [8]uint8 40 | } 41 | 42 | type SockaddrNetlink struct { 43 | Family uint16 44 | Pad uint16 45 | Pid uint32 46 | Groups uint32 47 | } 48 | 49 | type SockaddrUnix struct { 50 | Family uint16 51 | Path [108]byte 52 | } 53 | 54 | func Sockaddr(buf common.Buf, length int) syscall.Sockaddr { 55 | var port [2]byte 56 | order := buf.K.U.ByteOrder() 57 | // TODO: handle insufficient length 58 | var family uint16 59 | if err := buf.Unpack(&family); err != nil { 60 | return nil 61 | } 62 | // TODO: handle errors? 63 | st := buf.Struc() 64 | switch family { 65 | case AF_LOCAL: 66 | var a SockaddrUnix 67 | st.Unpack(&a) 68 | return sockaddrToNative(&a) 69 | case AF_INET: 70 | var a SockaddrInet4 71 | st.Unpack(&a) 72 | order.PutUint16(port[:], a.Port) 73 | a.Port = binary.BigEndian.Uint16(port[:]) 74 | return sockaddrToNative(&a) 75 | case AF_INET6: 76 | var a SockaddrInet6 77 | st.Unpack(&a) 78 | order.PutUint16(port[:], a.Port) 79 | a.Port = binary.BigEndian.Uint16(port[:]) 80 | return sockaddrToNative(&a) 81 | case AF_PACKET: 82 | var a SockaddrLinklayer 83 | st.Unpack(&a) 84 | return sockaddrToNative(&a) 85 | case AF_NETLINK: 86 | var a SockaddrNetlink 87 | st.Unpack(&a) 88 | return sockaddrToNative(&a) 89 | } 90 | return nil 91 | } 92 | -------------------------------------------------------------------------------- /go/kernel/darwin/unpack/sockaddr.go: -------------------------------------------------------------------------------- 1 | package unpack 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | "syscall" 7 | 8 | "github.com/lunixbochs/usercorn/go/kernel/common" 9 | ) 10 | 11 | const ( 12 | AF_LOCAL = 1 13 | AF_INET = 2 14 | AF_INET6 = 30 15 | AF_PACKET = 18 16 | ) 17 | 18 | type SockaddrHeader struct { 19 | Length uint8 20 | Family uint8 21 | } 22 | 23 | type SockaddrInet4 struct { 24 | Header SockaddrHeader 25 | Port uint16 26 | Addr [4]byte 27 | } 28 | 29 | type SockaddrInet6 struct { 30 | Header SockaddrHeader 31 | Port uint16 32 | Flowinfo uint32 33 | Addr [16]byte 34 | Scope_id uint32 35 | } 36 | 37 | type SockaddrLinklayer struct { 38 | Header SockaddrHeader 39 | Protocol uint16 40 | Ifindex int32 41 | Hatype uint16 42 | Pkttype uint8 43 | Halen uint8 44 | Addr [8]uint8 45 | } 46 | 47 | type SockaddrUnix struct { 48 | Header SockaddrHeader 49 | Path [108]byte 50 | } 51 | 52 | func Sockaddr(buf common.Buf, length int) syscall.Sockaddr { 53 | var port [2]byte 54 | order := buf.K.U.ByteOrder() 55 | // TODO: handle insufficient length 56 | var header SockaddrHeader 57 | if err := buf.Unpack(&header); err != nil { 58 | fmt.Println("unpack error", err) 59 | panic("sockaddr unpack error") 60 | return nil 61 | } 62 | // TODO: handle errors? 63 | st := buf.Struc() 64 | switch header.Family { 65 | case AF_LOCAL: 66 | var a SockaddrUnix 67 | st.Unpack(&a) 68 | return sockaddrToNative(&a) 69 | case AF_INET: 70 | var a SockaddrInet4 71 | st.Unpack(&a) 72 | order.PutUint16(port[:], a.Port) 73 | a.Port = binary.BigEndian.Uint16(port[:]) 74 | return sockaddrToNative(&a) 75 | case AF_INET6: 76 | var a SockaddrInet6 77 | st.Unpack(&a) 78 | order.PutUint16(port[:], a.Port) 79 | a.Port = binary.BigEndian.Uint16(port[:]) 80 | return sockaddrToNative(&a) 81 | case AF_PACKET: 82 | var a SockaddrLinklayer 83 | st.Unpack(&a) 84 | return sockaddrToNative(&a) 85 | default: 86 | fmt.Println("AF not known", header.Family) 87 | panic("unknown socket address family") 88 | } 89 | return nil 90 | } 91 | -------------------------------------------------------------------------------- /go/arch/sparc/arch.go: -------------------------------------------------------------------------------- 1 | package sparc 2 | 3 | import ( 4 | ks "github.com/keystone-engine/keystone/bindings/go/keystone" 5 | cs "github.com/lunixbochs/capstr" 6 | uc "github.com/unicorn-engine/unicorn/bindings/go/unicorn" 7 | 8 | "github.com/lunixbochs/usercorn/go/cpu" 9 | "github.com/lunixbochs/usercorn/go/cpu/unicorn" 10 | "github.com/lunixbochs/usercorn/go/models" 11 | ) 12 | 13 | var Arch = &models.Arch{ 14 | Name: "sparc", 15 | Bits: 32, 16 | Radare: "sparc", 17 | 18 | Cpu: &unicorn.Builder{Arch: uc.ARCH_SPARC, Mode: uc.MODE_SPARC32 | uc.MODE_BIG_ENDIAN}, 19 | Dis: &cpu.Capstr{Arch: cs.ARCH_SPARC, Mode: cs.MODE_BIG_ENDIAN}, 20 | Asm: &cpu.Keystone{Arch: ks.ARCH_SPARC, Mode: ks.MODE_SPARC32 | ks.MODE_BIG_ENDIAN}, 21 | 22 | PC: uc.SPARC_REG_PC, 23 | SP: uc.SPARC_REG_SP, 24 | Regs: map[string]int{ 25 | // "g0": uc.SPARC_REG_G0, // g0 is always zero 26 | "g1": uc.SPARC_REG_G1, 27 | "g2": uc.SPARC_REG_G2, 28 | "g3": uc.SPARC_REG_G3, 29 | "g4": uc.SPARC_REG_G4, 30 | "g5": uc.SPARC_REG_G5, 31 | "g6": uc.SPARC_REG_G6, 32 | "g7": uc.SPARC_REG_G7, 33 | "o0": uc.SPARC_REG_O0, 34 | "o1": uc.SPARC_REG_O1, 35 | "o2": uc.SPARC_REG_O2, 36 | "o3": uc.SPARC_REG_O3, 37 | "o4": uc.SPARC_REG_O4, 38 | "o5": uc.SPARC_REG_O5, 39 | "o6": uc.SPARC_REG_O6, // sp 40 | "o7": uc.SPARC_REG_O7, 41 | "l0": uc.SPARC_REG_L0, 42 | "l1": uc.SPARC_REG_L1, 43 | "l2": uc.SPARC_REG_L2, 44 | "l3": uc.SPARC_REG_L3, 45 | "l4": uc.SPARC_REG_L4, 46 | "l5": uc.SPARC_REG_L5, 47 | "l6": uc.SPARC_REG_L6, 48 | "l7": uc.SPARC_REG_L7, 49 | "i0": uc.SPARC_REG_I0, 50 | "i1": uc.SPARC_REG_I1, 51 | "i2": uc.SPARC_REG_I2, 52 | "i3": uc.SPARC_REG_I3, 53 | "i4": uc.SPARC_REG_I4, 54 | "i5": uc.SPARC_REG_I5, 55 | "i6": uc.SPARC_REG_I6, // fp 56 | "i7": uc.SPARC_REG_I7, 57 | 58 | "sp": uc.SPARC_REG_SP, 59 | "fp": uc.SPARC_REG_FP, 60 | }, 61 | DefaultRegs: []string{ 62 | "g1", "g2", "g3", "g4", "g5", "g6", "g7", 63 | "o0", "o1", "o2", "o3", "o4", "o5", "o7", 64 | "l0", "l1", "l2", "l3", "l4", "l5", "l6", "l7", 65 | "i0", "i1", "i2", "i3", "i4", "i5", "i7", 66 | }, 67 | } 68 | -------------------------------------------------------------------------------- /go/models/cpu/regs.go: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import ( 4 | "github.com/pkg/errors" 5 | ) 6 | 7 | // implements register and context methods conforming to cpu.Cpu 8 | // TODO: maps are slow. 9 | // []uint64 would be faster, but enum lookups would require a second []bool (to check if an enum is valid) 10 | // and the array size could become very large if someone uses a large enum 11 | // another option would be to switch between map and array based on max enum 12 | // and yet another would be to reject too-large enums (force uint16 in initialization?) 13 | type Regs struct { 14 | mask uint64 15 | vals map[int]uint64 16 | } 17 | 18 | func NewRegs(bits uint, enums []int) *Regs { 19 | r := &Regs{ 20 | mask: ^uint64(0) >> (64 - bits), 21 | vals: make(map[int]uint64), 22 | } 23 | for _, e := range enums { 24 | r.vals[e] = 0 25 | } 26 | return r 27 | } 28 | 29 | func (r *Regs) RegRead(enum int) (uint64, error) { 30 | if val, ok := r.vals[enum]; !ok { 31 | return 0, errors.New("invalid register") 32 | } else { 33 | return val, nil 34 | } 35 | } 36 | 37 | func (r *Regs) RegWrite(enum int, val uint64) error { 38 | val &= r.mask 39 | if _, ok := r.vals[enum]; !ok { 40 | return errors.New("invalid register") 41 | } 42 | r.vals[enum] = val 43 | return nil 44 | } 45 | 46 | // handling ContextSave in the register file either requires you to store important cpu state (like flags) in registers 47 | // or wrap ContextSave/ContextRestore with your own functions 48 | func (r *Regs) ContextSave(reuse interface{}) (interface{}, error) { 49 | var m map[int]uint64 50 | if reuse != nil { 51 | var ok bool 52 | if m, ok = reuse.(map[int]uint64); !ok { 53 | return nil, errors.New("incorrect context type") 54 | } 55 | } else { 56 | m = make(map[int]uint64) 57 | } 58 | for k, v := range r.vals { 59 | m[k] = v 60 | } 61 | return m, nil 62 | } 63 | 64 | func (r *Regs) ContextRestore(ctx interface{}) error { 65 | if m, ok := ctx.(map[int]uint64); !ok { 66 | return errors.New("incorrect context type") 67 | } else { 68 | for k, v := range m { 69 | r.vals[k] = v 70 | } 71 | return nil 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /go/native/statfs_darwin64.go: -------------------------------------------------------------------------------- 1 | // +build amd64 2 | // +build darwin 3 | 4 | package native 5 | 6 | import ( 7 | "syscall" 8 | ) 9 | 10 | func StatfsToLinux(s *syscall.Statfs_t) *LinuxStatfs_t { 11 | return &LinuxStatfs_t{ 12 | Type: int64(s.Type), 13 | Bsize: int64(s.Bsize), 14 | Blocks: uint64(s.Blocks), 15 | Bfree: uint64(s.Bfree), 16 | Bavail: uint64(s.Bavail), 17 | Files: uint64(s.Files), 18 | Ffree: uint64(s.Ffree), 19 | Fsid: s.Fsid, 20 | // MAXPATH on 64-bit Darwin is 1024 21 | Namelen: 1024, 22 | // FIXME: unknown 23 | // Frsize: s.Frsize, 24 | // FIXME: flags are almost certainly wrong 25 | Flags: int64(s.Flags), 26 | } 27 | } 28 | 29 | func copyIntToByteSlice(dst []byte, src []int8) { 30 | length := len(dst) 31 | if len(src) < len(dst) { 32 | length = len(src) 33 | } 34 | for i := 0; i < length; i++ { 35 | dst[i] = byte(src[i]) 36 | } 37 | } 38 | 39 | func StatfsToDarwin32(s *syscall.Statfs_t) *DarwinStatfs32_t { 40 | ret := &DarwinStatfs32_t{ 41 | Bsize: int(s.Bsize), 42 | Iosize: int(s.Iosize), 43 | Blocks: int(s.Blocks), 44 | Bfree: int(s.Bfree), 45 | Bavail: int(s.Bavail), 46 | Files: int(s.Files), 47 | Ffree: int(s.Ffree), 48 | Fsid: s.Fsid, 49 | // FIXME: type is truncated, maybe pick a default type 50 | Type: int16(s.Type), 51 | Flags: int32(s.Flags), 52 | } 53 | copyIntToByteSlice(ret.Fstypename[:], s.Fstypename[:]) 54 | copyIntToByteSlice(ret.Mntonname[:], s.Mntonname[:]) 55 | copyIntToByteSlice(ret.Mntfromname[:], s.Mntfromname[:]) 56 | return ret 57 | } 58 | 59 | func StatfsToDarwin64(s *syscall.Statfs_t) *DarwinStatfs64_t { 60 | ret := &DarwinStatfs64_t{ 61 | Bsize: uint32(s.Bsize), 62 | Iosize: int32(s.Iosize), 63 | Blocks: uint64(s.Blocks), 64 | Bfree: uint64(s.Bfree), 65 | Bavail: uint64(s.Bavail), 66 | Files: uint64(s.Files), 67 | Ffree: uint64(s.Ffree), 68 | Fsid: s.Fsid, 69 | Type: uint32(s.Type), 70 | Flags: uint32(s.Flags), 71 | } 72 | copyIntToByteSlice(ret.Fstypename[:], s.Fstypename[:]) 73 | copyIntToByteSlice(ret.Mntonname[:], s.Mntonname[:]) 74 | copyIntToByteSlice(ret.Mntfromname[:], s.Mntfromname[:]) 75 | return ret 76 | } 77 | -------------------------------------------------------------------------------- /go/models/async_stream.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "github.com/pkg/errors" 5 | "io" 6 | "time" 7 | ) 8 | 9 | type AsyncStream struct { 10 | io.WriteCloser 11 | closed bool 12 | move chan io.WriteCloser 13 | close chan chan int 14 | write chan []byte 15 | 16 | buffer [][]byte 17 | count uint64 18 | } 19 | 20 | func NewAsyncStream(w io.WriteCloser) io.WriteCloser { 21 | a := &AsyncStream{ 22 | WriteCloser: w, 23 | closed: false, 24 | move: make(chan io.WriteCloser), 25 | close: make(chan chan int), 26 | write: make(chan []byte, 1000), 27 | } 28 | go a.run() 29 | return a 30 | } 31 | 32 | func (a *AsyncStream) flush() { 33 | for _, p := range a.buffer { 34 | if _, err := a.WriteCloser.Write(p); err != nil { 35 | a.closed = true 36 | break 37 | } 38 | } 39 | a.buffer = a.buffer[:0] 40 | a.count = 0 41 | } 42 | 43 | func (a *AsyncStream) run() { 44 | duration := 25 * time.Millisecond 45 | t := time.NewTimer(duration) 46 | timer := false 47 | for !a.closed { 48 | select { 49 | case <-t.C: 50 | a.flush() 51 | timer = false 52 | case p := <-a.write: 53 | a.buffer = append(a.buffer, p) 54 | a.count += uint64(len(p)) 55 | if len(a.buffer) > 1000 || a.count > 64000 { 56 | a.flush() 57 | } 58 | case w := <-a.move: 59 | a.WriteCloser = w 60 | case tmp := <-a.close: 61 | a.flush() 62 | a.WriteCloser.Close() 63 | a.closed = true 64 | tmp <- 1 65 | break 66 | } 67 | if len(a.buffer) > 0 && !timer { 68 | timer = true 69 | t.Reset(duration) 70 | } 71 | } 72 | if !t.Stop() { 73 | <-t.C 74 | } 75 | } 76 | 77 | func (a *AsyncStream) Move(w io.WriteCloser) { 78 | a.move <- w 79 | } 80 | 81 | func (a *AsyncStream) Write(p []byte) (int, error) { 82 | if a.closed { 83 | return 0, errors.New("async stream is closed") 84 | } 85 | tmp := make([]byte, len(p)) 86 | copy(tmp, p) 87 | a.write <- tmp 88 | return len(tmp), nil 89 | } 90 | 91 | func (a *AsyncStream) Close() error { 92 | if a.closed { 93 | return errors.New("async stream was already closed") 94 | } else { 95 | tmp := make(chan int) 96 | a.close <- tmp 97 | <-tmp 98 | } 99 | return nil 100 | } 101 | -------------------------------------------------------------------------------- /go/lua/print.go: -------------------------------------------------------------------------------- 1 | package lua 2 | 3 | import ( 4 | "fmt" 5 | "github.com/lunixbochs/luaish" 6 | "strings" 7 | ) 8 | 9 | func strslen(strs []string) int { 10 | i := 0 11 | for _, v := range strs { 12 | i += len(v) 13 | } 14 | return i 15 | } 16 | 17 | func (L *LuaRepl) prettydump(lv []lua.LValue, implicit bool, outer bool, seen map[lua.LValue]bool) []string { 18 | pretty := make([]string, len(lv)) 19 | for i, v := range lv { 20 | switch s := v.(type) { 21 | case *lua.LTable: 22 | // seen[v] is used to skip recursive table references 23 | if _, ok := seen[v]; ok { 24 | pretty[i] = "{\"\"]}" 25 | continue 26 | } 27 | seen[v] = true 28 | 29 | table := make([]string, 0, s.Len()) 30 | idx := 1 31 | s.ForEach(func(k, v lua.LValue) { 32 | tmp := L.prettydump([]lua.LValue{k, v}, implicit, false, seen) 33 | if n, ok := k.(lua.LInt); ok && int(n) == idx { 34 | idx += 1 35 | table = append(table, tmp[1]) 36 | } else { 37 | table = append(table, strings.Join(tmp, " = ")) 38 | } 39 | }) 40 | 41 | seen[v] = false 42 | if outer { 43 | pretty[i] = "{" + strings.Join(table, ",\n ") + "}" 44 | } else { 45 | pretty[i] = "{" + strings.Join(table, ", ") + "}" 46 | } 47 | case lua.LFloat: 48 | pretty[i] = fmt.Sprintf("%f", float64(s)) 49 | case lua.LInt: 50 | n := uint64(s) 51 | if n < 10 { 52 | pretty[i] = fmt.Sprintf("%d", n) 53 | } else if n > 0x10000 { 54 | pretty[i] = fmt.Sprintf("%#x", n) 55 | } else { 56 | pretty[i] = fmt.Sprintf("%#x(%d)", n, n) 57 | } 58 | case lua.LString: 59 | // for some reason repr doesn't print out null bytes properly 60 | // pretty[i] = models.Repr([]byte(s), 0) 61 | if implicit { 62 | pretty[i] = fmt.Sprintf("%#v", s) 63 | } else { 64 | pretty[i] = string(s) 65 | } 66 | default: 67 | pretty[i] = fmt.Sprintf("%s", s) 68 | } 69 | } 70 | return pretty 71 | } 72 | 73 | func (L *LuaRepl) PrettyDump(lv []lua.LValue, implicit bool, outer bool) []string { 74 | return L.prettydump(lv, implicit, outer, make(map[lua.LValue]bool)) 75 | } 76 | 77 | func (L *LuaRepl) PrettyPrint(lv []lua.LValue, implicit bool) { 78 | pretty := L.PrettyDump(lv, implicit, true) 79 | L.Printf("%s\n", strings.Join(pretty, " ")) 80 | } 81 | -------------------------------------------------------------------------------- /go/arch/arm64/arch.go: -------------------------------------------------------------------------------- 1 | package arm64 2 | 3 | import ( 4 | ks "github.com/keystone-engine/keystone/bindings/go/keystone" 5 | cs "github.com/lunixbochs/capstr" 6 | uc "github.com/unicorn-engine/unicorn/bindings/go/unicorn" 7 | 8 | "github.com/lunixbochs/usercorn/go/cpu" 9 | "github.com/lunixbochs/usercorn/go/cpu/unicorn" 10 | "github.com/lunixbochs/usercorn/go/models" 11 | ) 12 | 13 | var Arch = &models.Arch{ 14 | Name: "arm64", 15 | Bits: 64, 16 | Radare: "arm64", 17 | 18 | Cpu: &unicorn.Builder{Arch: uc.ARCH_ARM64, Mode: uc.MODE_ARM}, 19 | Dis: &cpu.Capstr{Arch: cs.ARCH_ARM64, Mode: cs.MODE_ARM}, 20 | Asm: &cpu.Keystone{Arch: ks.ARCH_ARM64, Mode: ks.MODE_LITTLE_ENDIAN}, 21 | 22 | PC: uc.ARM64_REG_PC, 23 | SP: uc.ARM64_REG_SP, 24 | Regs: map[string]int{ 25 | "x0": uc.ARM64_REG_X0, 26 | "x1": uc.ARM64_REG_X1, 27 | "x2": uc.ARM64_REG_X2, 28 | "x3": uc.ARM64_REG_X3, 29 | "x4": uc.ARM64_REG_X4, 30 | "x5": uc.ARM64_REG_X5, 31 | "x6": uc.ARM64_REG_X6, 32 | "x7": uc.ARM64_REG_X7, 33 | "x8": uc.ARM64_REG_X8, 34 | "x9": uc.ARM64_REG_X9, 35 | "x10": uc.ARM64_REG_X10, 36 | "x11": uc.ARM64_REG_X11, 37 | "x12": uc.ARM64_REG_X12, 38 | "x13": uc.ARM64_REG_X13, 39 | "x14": uc.ARM64_REG_X14, 40 | "x15": uc.ARM64_REG_X15, 41 | "x16": uc.ARM64_REG_X16, 42 | "x17": uc.ARM64_REG_X17, 43 | "x18": uc.ARM64_REG_X18, 44 | "x19": uc.ARM64_REG_X19, 45 | "x20": uc.ARM64_REG_X20, 46 | "x21": uc.ARM64_REG_X21, 47 | "x22": uc.ARM64_REG_X22, 48 | "x23": uc.ARM64_REG_X23, 49 | "x24": uc.ARM64_REG_X24, 50 | "x25": uc.ARM64_REG_X25, 51 | "x26": uc.ARM64_REG_X26, 52 | "x27": uc.ARM64_REG_X27, 53 | "x28": uc.ARM64_REG_X28, 54 | "fp": uc.ARM64_REG_FP, 55 | "lr": uc.ARM64_REG_LR, 56 | "sp": uc.ARM64_REG_SP, 57 | "pc": uc.ARM64_REG_PC, 58 | }, 59 | DefaultRegs: []string{ 60 | "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", 61 | "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", 62 | "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", 63 | "x25", "x26", "x27", "x28", 64 | }, 65 | } 66 | 67 | func EnableFPU(u models.Usercorn) error { 68 | val, err := u.RegRead(uc.ARM64_REG_CPACR_EL1) 69 | if err != nil { 70 | return err 71 | } 72 | val |= 0x300000 // set the FPEN bits 73 | return u.RegWrite(uc.ARM64_REG_CPACR_EL1, val) 74 | } 75 | -------------------------------------------------------------------------------- /go/kernel/common/trace.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "strings" 7 | 8 | "github.com/lunixbochs/usercorn/go/models" 9 | ) 10 | 11 | func (s Syscall) traceArg(args ...interface{}) string { 12 | hex := func(a interface{}) string { 13 | tmp := fmt.Sprintf("0x%x", a) 14 | if strings.HasPrefix(tmp, "0x-") { 15 | tmp = "-0x" + tmp[3:] 16 | } 17 | return tmp 18 | } 19 | 20 | switch arg := args[0].(type) { 21 | case Obuf: 22 | return hex(arg.Addr) 23 | case Buf: 24 | if len(args) > 1 { 25 | if length, ok := args[1].(Len); ok { 26 | mem, _ := s.Kernel.U.MemRead(arg.Addr, uint64(length)) 27 | return models.Repr(mem, s.Kernel.U.Config().Strsize) 28 | } 29 | } 30 | return hex(arg.Addr) 31 | case Off: 32 | return hex(arg) 33 | case Ptr: 34 | return hex(arg) 35 | case Fd: 36 | return fmt.Sprintf("%d", int32(arg)) 37 | case string: 38 | return models.Repr([]byte(arg), s.Kernel.U.Config().Strsize) 39 | case uint64: 40 | return hex(arg) 41 | default: 42 | return fmt.Sprintf("%v", arg) 43 | } 44 | } 45 | 46 | func (s Syscall) traceArgs(regs []uint64) string { 47 | inRef, err := s.Kernel.Argjoy.Convert(s.In, false, regs) 48 | if err != nil { 49 | return err.Error() 50 | } 51 | in := make([]interface{}, len(inRef)) 52 | for i, val := range inRef { 53 | in[i] = val.Interface() 54 | } 55 | ret := make([]string, len(in)) 56 | for i := range in { 57 | ret[i] = s.traceArg(in[i:]...) 58 | } 59 | return strings.Join(ret, ", ") 60 | } 61 | 62 | func (s Syscall) Trace(regs []uint64) string { 63 | return fmt.Sprintf("%s(%s)", s.Name, s.traceArgs(regs)) 64 | } 65 | 66 | func (s Syscall) TraceRet(args []uint64, ret uint64) string { 67 | var out []string 68 | for i, typ := range s.In { 69 | if typ == reflect.TypeOf(Obuf{}) && len(args) > i+1 { 70 | length := int(ret) 71 | if uint64(length) <= args[i+1] && length >= 0 { 72 | mem, _ := s.Kernel.U.MemRead(args[i], uint64(length)) 73 | out = append(out, models.Repr(mem, s.Kernel.U.Config().Strsize)) 74 | } 75 | } 76 | } 77 | if len(s.Out) > 0 { 78 | // TODO: need a standard for converting return values 79 | out = append(out, s.traceArg(ret)) 80 | } 81 | if len(out) > 0 { 82 | return fmt.Sprintf(" = %s", strings.Join(out, ", ")) 83 | } else { 84 | return "" 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /go/arch/x86/arch.go: -------------------------------------------------------------------------------- 1 | package x86 2 | 3 | import ( 4 | ks "github.com/keystone-engine/keystone/bindings/go/keystone" 5 | cs "github.com/lunixbochs/capstr" 6 | uc "github.com/unicorn-engine/unicorn/bindings/go/unicorn" 7 | 8 | "github.com/lunixbochs/usercorn/go/cpu" 9 | "github.com/lunixbochs/usercorn/go/cpu/unicorn" 10 | "github.com/lunixbochs/usercorn/go/models" 11 | ) 12 | 13 | var Arch = &models.Arch{ 14 | Name: "x86", 15 | Bits: 32, 16 | Radare: "x86", 17 | 18 | Cpu: &unicorn.Builder{Arch: uc.ARCH_X86, Mode: uc.MODE_32}, 19 | Dis: &cpu.Capstr{Arch: cs.ARCH_X86, Mode: cs.MODE_32}, 20 | Asm: &cpu.Keystone{Arch: ks.ARCH_X86, Mode: ks.MODE_32}, 21 | 22 | PC: uc.X86_REG_EIP, 23 | SP: uc.X86_REG_ESP, 24 | Regs: map[string]int{ 25 | "eip": uc.X86_REG_EIP, 26 | "esp": uc.X86_REG_ESP, 27 | "ebp": uc.X86_REG_EBP, 28 | "eax": uc.X86_REG_EAX, 29 | "ebx": uc.X86_REG_EBX, 30 | "ecx": uc.X86_REG_ECX, 31 | "edx": uc.X86_REG_EDX, 32 | "esi": uc.X86_REG_ESI, 33 | "edi": uc.X86_REG_EDI, 34 | 35 | "eflags": uc.X86_REG_EFLAGS, 36 | 37 | "cs": uc.X86_REG_CS, 38 | "ds": uc.X86_REG_DS, 39 | "es": uc.X86_REG_ES, 40 | "fs": uc.X86_REG_FS, 41 | "gs": uc.X86_REG_GS, 42 | "ss": uc.X86_REG_SS, 43 | 44 | /* // TODO: can't handle 80-bit regs 45 | "st0": uc.X86_REG_ST0, 46 | "st1": uc.X86_REG_ST0, 47 | "st2": uc.X86_REG_ST0, 48 | "st3": uc.X86_REG_ST0, 49 | "st4": uc.X86_REG_ST0, 50 | "st5": uc.X86_REG_ST0, 51 | "st6": uc.X86_REG_ST0, 52 | "st7": uc.X86_REG_ST0, 53 | */ 54 | }, 55 | DefaultRegs: []string{ 56 | "eax", "ebx", "ecx", "edx", "esi", "edi", "ebp", 57 | }, 58 | GdbXml: gdbXml, 59 | } 60 | 61 | func Wrmsr(u models.Usercorn, msr, value uint64) { 62 | u.RunAsm( 63 | 0, "wrmsr", 64 | map[int]uint64{ 65 | uc.X86_REG_RAX: value & 0xFFFFFFFF, 66 | uc.X86_REG_RDX: value >> 32 & 0xFFFFFFFF, 67 | uc.X86_REG_RCX: msr & 0xFFFFFFFF, 68 | }, nil, 69 | ) 70 | } 71 | 72 | func Rdmsr(u models.Usercorn, msr uint64) uint64 { 73 | rcx, _ := u.RegRead(uc.X86_REG_RCX) 74 | rdx, _ := u.RegRead(uc.X86_REG_RDX) 75 | 76 | u.RunAsm(0, "rdmsr", map[int]uint64{uc.X86_REG_RAX: msr}, nil) 77 | ecx, _ := u.RegRead(uc.X86_REG_ECX) 78 | edx, _ := u.RegRead(uc.X86_REG_EDX) 79 | 80 | u.RegWrite(uc.X86_REG_RCX, rcx) 81 | u.RegWrite(uc.X86_REG_RDX, rdx) 82 | return (edx << 32) | (ecx & 0xFFFFFFFF) 83 | } 84 | -------------------------------------------------------------------------------- /go/cmd/trace/ecov.go: -------------------------------------------------------------------------------- 1 | package trace 2 | 3 | import ( 4 | "fmt" 5 | "github.com/pkg/errors" 6 | "io" 7 | "os" 8 | "sort" 9 | 10 | "github.com/lunixbochs/usercorn/go/arch" 11 | "github.com/lunixbochs/usercorn/go/models" 12 | "github.com/lunixbochs/usercorn/go/models/cpu" 13 | "github.com/lunixbochs/usercorn/go/models/debug" 14 | "github.com/lunixbochs/usercorn/go/models/trace" 15 | ) 16 | 17 | // TODO: this is duplicated from drcov.go 18 | func WriteEcov(tf *trace.TraceReader, out *os.File) error { 19 | arch, OS, err := arch.GetArch(tf.Header.Arch, tf.Header.OS) 20 | if err != nil { 21 | return errors.Wrap(err, "arch.GetArch() failed") 22 | } 23 | config := &models.Config{} 24 | config.Init() 25 | replay := trace.NewReplay(arch, OS, tf.Header.CodeOrder, debug.NewDebug(tf.Header.Arch, config)) 26 | 27 | modserial := 0 28 | modules := make(map[string]*cpu.Page) 29 | modlookup := make(cpu.Pages, 0) 30 | 31 | addmod := func(o *trace.OpMemMap) { 32 | if o.Prot&cpu.PROT_EXEC == 0 { 33 | return 34 | } 35 | key := o.Desc + "|" + o.File 36 | if _, ok := modules[key]; !ok { 37 | desc := o.File 38 | if desc == "" { 39 | desc = "[" + o.Desc + "]" 40 | } 41 | mod := &cpu.Page{Addr: o.Addr, Size: o.Size, Prot: modserial, Desc: desc} 42 | modules[key] = mod 43 | modlookup = append(modlookup, mod) 44 | modserial++ 45 | } 46 | sort.Sort(modlookup) 47 | } 48 | 49 | for { 50 | op, err := tf.Next() 51 | if err == io.EOF { 52 | break 53 | } else if err != nil { 54 | fmt.Println(errors.Wrap(err, "error reading next trace operation")) 55 | break 56 | } 57 | switch frame := op.(type) { 58 | case *trace.OpKeyframe: 59 | for _, op := range frame.Ops { 60 | switch o := op.(type) { 61 | case *trace.OpMemMap: 62 | addmod(o) 63 | } 64 | } 65 | case *trace.OpFrame: 66 | for _, op := range frame.Ops { 67 | switch o := op.(type) { 68 | case *trace.OpJmp: 69 | mod := modlookup.Find(o.Addr) 70 | if mod != nil && mod.Prot == 0 { 71 | fmt.Fprintf(out, "%x\n", o.Addr-mod.Addr) 72 | } 73 | case *trace.OpMemMap: 74 | addmod(o) 75 | case *trace.OpSyscall: 76 | for _, op := range o.Ops { 77 | switch o := op.(type) { 78 | case *trace.OpMemMap: 79 | addmod(o) 80 | } 81 | } 82 | } 83 | } 84 | } 85 | } 86 | replay.Flush() 87 | return nil 88 | } 89 | -------------------------------------------------------------------------------- /go/models/usercorn.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "github.com/lunixbochs/ghostrace/ghost/memio" 5 | 6 | "github.com/lunixbochs/usercorn/go/models/cpu" 7 | ) 8 | 9 | type SysGetArgs func(n int) ([]uint64, error) 10 | type SysCb func(num int, name string, args []uint64, ret uint64, desc string) bool 11 | type SysHook struct { 12 | Before, After SysCb 13 | } 14 | 15 | type MapCb func(addr, size uint64, prot int, desc string, file *cpu.FileDesc) 16 | type UnmapCb func(addr, size uint64) 17 | type ProtCb func(addr, size uint64, prot int) 18 | type MapHook struct { 19 | Map MapCb 20 | Unmap UnmapCb 21 | Prot ProtCb 22 | } 23 | 24 | type Usercorn interface { 25 | Task 26 | Config() *Config 27 | Run() error 28 | Trampoline(func() error) error 29 | 30 | Callstack() []Stackframe 31 | Restart(func(Usercorn, error) error) 32 | Rewind(n, addr uint64) error 33 | 34 | Gate() *Gate 35 | 36 | Printf(fmt string, args ...interface{}) 37 | Println(s ...interface{}) 38 | 39 | Brk(addr uint64) (uint64, error) 40 | Mem() memio.MemIO 41 | MapStack(base uint64, size uint64, guard bool) error 42 | StrucAt(addr uint64) *StrucStream 43 | 44 | DirectRead(addr, size uint64) ([]byte, error) 45 | DirectWrite(addr uint64, p []byte) error 46 | 47 | RunShellcodeMapped(addr uint64, code []byte, setRegs map[int]uint64, regsClobbered []int) error 48 | RunShellcode(addr uint64, code []byte, setRegs map[int]uint64, regsClobbered []int) error 49 | RunAsm(addr uint64, asm string, setRegs map[int]uint64, regsClobbered []int) error 50 | 51 | BreakAdd(desc string, future bool, cb func(u Usercorn, addr uint64)) (*Breakpoint, error) 52 | BreakDel(b *Breakpoint) error 53 | Breakpoints() []*Breakpoint 54 | Symbolicate(addr uint64, includeSource bool) (*Symbol, string) 55 | 56 | Exe() string 57 | Loader() Loader 58 | InterpBase() uint64 59 | Base() uint64 60 | Entry() uint64 61 | BinEntry() uint64 62 | SetEntry(entry uint64) 63 | SetExit(exit uint64) 64 | 65 | // TODO: PrefixPath will be replaced by a full VFS subsystem 66 | PrefixPath(s string, force bool) string 67 | 68 | HookSysAdd(before, after SysCb) *SysHook 69 | HookSysDel(cb *SysHook) 70 | HookMapAdd(mapCb MapCb, unmapCb UnmapCb, protCb ProtCb) *MapHook 71 | HookMapDel(cb *MapHook) 72 | 73 | AddKernel(kernel interface{}, first bool) 74 | Syscall(num int, name string, getArgs SysGetArgs) (uint64, error) 75 | 76 | Inscount() uint64 77 | 78 | Exit(err error) 79 | } 80 | -------------------------------------------------------------------------------- /go/arch/arm/arch.go: -------------------------------------------------------------------------------- 1 | package arm 2 | 3 | import ( 4 | ks "github.com/keystone-engine/keystone/bindings/go/keystone" 5 | cs "github.com/lunixbochs/capstr" 6 | uc "github.com/unicorn-engine/unicorn/bindings/go/unicorn" 7 | 8 | "github.com/lunixbochs/usercorn/go/cpu" 9 | "github.com/lunixbochs/usercorn/go/cpu/unicorn" 10 | "github.com/lunixbochs/usercorn/go/models" 11 | ) 12 | 13 | var Arch = &models.Arch{ 14 | Name: "arm", 15 | Bits: 32, 16 | Radare: "arm", 17 | 18 | Cpu: &unicorn.Builder{Arch: uc.ARCH_ARM, Mode: uc.MODE_ARM}, 19 | Dis: &cpu.Capstr{Arch: cs.ARCH_ARM, Mode: cs.MODE_ARM}, 20 | Asm: &cpu.Keystone{Arch: ks.ARCH_ARM, Mode: ks.MODE_ARM}, 21 | 22 | PC: uc.ARM_REG_PC, 23 | SP: uc.ARM_REG_SP, 24 | Regs: map[string]int{ 25 | "r0": uc.ARM_REG_R0, 26 | "r1": uc.ARM_REG_R1, 27 | "r2": uc.ARM_REG_R2, 28 | "r3": uc.ARM_REG_R3, 29 | "r4": uc.ARM_REG_R4, 30 | "r5": uc.ARM_REG_R5, 31 | "r6": uc.ARM_REG_R6, 32 | "r7": uc.ARM_REG_R7, 33 | "r8": uc.ARM_REG_R8, 34 | "r9": uc.ARM_REG_R9, 35 | "r10": uc.ARM_REG_R10, 36 | "r11": uc.ARM_REG_R11, 37 | "r12": uc.ARM_REG_R12, 38 | "lr": uc.ARM_REG_LR, 39 | "sp": uc.ARM_REG_SP, 40 | "pc": uc.ARM_REG_PC, 41 | }, 42 | DefaultRegs: []string{ 43 | "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", 44 | "r9", "r10", "r11", "r12", 45 | }, 46 | } 47 | 48 | func EnterUsermode(u models.Usercorn) error { 49 | // move CPU from System to User mode 50 | modeSwitchAsm := ` 51 | mrs r0, cpsr 52 | bic r0, r0, $0x1f 53 | orr r0, r0, $0x10 54 | msr cpsr_c, r0 55 | ` 56 | modeSwitch, err := u.Asm(modeSwitchAsm, 0) 57 | if err != nil { 58 | return err 59 | } 60 | // this is manually mapped instead of using RunShellcode() so 61 | // the link register will be set to exit the emulator correctly 62 | size := uint64(len(modeSwitch)) 63 | addr, err := u.Malloc(size, "shellcode") 64 | if err != nil { 65 | return err 66 | } 67 | u.MemProt(addr, size, uc.PROT_ALL) 68 | defer u.MemUnmap(addr, size) 69 | end := addr + size 70 | return u.RunShellcodeMapped(addr, modeSwitch, 71 | map[int]uint64{uc.ARM_REG_LR: end}, 72 | []int{uc.ARM_REG_R0, uc.ARM_REG_LR, uc.ARM_REG_SP}, 73 | ) 74 | } 75 | 76 | func EnableFPU(u models.Usercorn) error { 77 | val, err := u.RegRead(uc.ARM_REG_C1_C0_2) 78 | if err != nil { 79 | return err 80 | } 81 | if err = u.RegWrite(uc.ARM_REG_C1_C0_2, val|(0xf<<20)); err != nil { 82 | return err 83 | } 84 | return u.RegWrite(uc.ARM_REG_FPEXC, 0x40000000) 85 | } 86 | -------------------------------------------------------------------------------- /go/kernel/posix/mman.go: -------------------------------------------------------------------------------- 1 | package posix 2 | 3 | import ( 4 | "io" 5 | "os" 6 | "syscall" 7 | 8 | co "github.com/lunixbochs/usercorn/go/kernel/common" 9 | "github.com/lunixbochs/usercorn/go/models/cpu" 10 | "github.com/lunixbochs/usercorn/go/native/enum" 11 | ) 12 | 13 | func (k *PosixKernel) Mmap(addrHint, size uint64, prot enum.MmapProt, flags enum.MmapFlag, fd co.Fd, off co.Off) uint64 { 14 | // TODO: how do we request an enum lookup from the current kernel? 15 | MAP_FIXED := enum.MmapFlag(0x10) // on OS X and Linux anyway 16 | // TODO: MAP_FIXED means abort if we can't get the address 17 | var ( 18 | data []byte 19 | path string 20 | err error 21 | file *os.File 22 | 23 | fileDesc *cpu.FileDesc 24 | ) 25 | // if there's a file descriptor, map (copy for now) the file here before messing with guest memory 26 | if fd > 0 { 27 | fd2, _ := syscall.Dup(int(fd)) 28 | if file, ok := k.Files[fd]; ok { 29 | path = file.Path 30 | } 31 | if path != "" { 32 | fileDesc = &cpu.FileDesc{Name: path, Off: uint64(off), Len: size} 33 | file = os.NewFile(uintptr(fd2), path) 34 | defer file.Close() 35 | data = make([]byte, size) 36 | var pos uint64 37 | for pos < size { 38 | n, err := file.ReadAt(data[pos:], int64(off)+int64(pos)) 39 | pos += uint64(n) 40 | if err == io.EOF { 41 | break 42 | } else if err != nil { 43 | return UINT64_MAX // FIXME 44 | } 45 | } 46 | data = data[:pos] 47 | } 48 | } 49 | fixed := flags&MAP_FIXED != 0 50 | if addrHint == 0 && !fixed { 51 | // don't automap memory within 8MB of the current program break 52 | brk, _ := k.U.Brk(0) 53 | addrHint = brk + 0x800000 54 | } 55 | addr, err := k.U.Mmap(addrHint, size, int(prot), fixed, "mmap", fileDesc) 56 | if err != nil { 57 | return UINT64_MAX // FIXME 58 | } 59 | if fd > 0 && data != nil { 60 | err := k.U.MemWrite(addr, data) 61 | if err != nil { 62 | return UINT64_MAX // FIXME 63 | } 64 | } 65 | return addr 66 | } 67 | 68 | func (k *PosixKernel) Munmap(addr, size uint64) uint64 { 69 | // TODO: page size 70 | if addr&0xfff != 0 { 71 | return UINT64_MAX // FIXME: EINVAL 72 | } 73 | k.U.MemUnmap(addr, size) 74 | return 0 75 | } 76 | 77 | func (k *PosixKernel) Mprotect(addr, size uint64, prot enum.MmapProt) uint64 { 78 | // FIXME: Issue #137 79 | prot = enum.MmapProt(cpu.PROT_ALL) 80 | if err := k.U.MemProt(addr, size, int(prot)); err != nil { 81 | return UINT64_MAX // FIXME 82 | } 83 | return 0 84 | } 85 | 86 | func (k *PosixKernel) Brk(addr uint64) uint64 { 87 | // TODO: return is Linux specific 88 | ret, _ := k.U.Brk(addr) 89 | return ret 90 | } 91 | -------------------------------------------------------------------------------- /go/models/cpu/regs_test.go: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func makeRegs(bits uint) ([]int, *Regs) { 8 | enums := make([]int, 100) 9 | for i := range enums { 10 | enums[i] = 100 - i 11 | } 12 | return enums, NewRegs(bits, enums) 13 | } 14 | 15 | func BenchmarkRegsRead(b *testing.B) { 16 | enums, regs := makeRegs(64) 17 | for i := 0; i < b.N; i++ { 18 | regs.RegRead(enums[i%len(enums)]) 19 | } 20 | } 21 | 22 | func BenchmarkRegsWrite(b *testing.B) { 23 | enums, regs := makeRegs(64) 24 | for i := 0; i < b.N; i++ { 25 | regs.RegWrite(enums[i%len(enums)], uint64(i)) 26 | } 27 | } 28 | 29 | func TestRegs(t *testing.T) { 30 | enums, regs := makeRegs(64) 31 | 32 | // save context to check zeroes later 33 | ctx, err := regs.ContextSave(nil) 34 | if err != nil { 35 | t.Fatal(err, "initial ContextSave() failed") 36 | } 37 | 38 | // set all regs to pos * 2 39 | for i, e := range enums { 40 | if err := regs.RegWrite(e, uint64(i*2)); err != nil { 41 | t.Fatal(err, "initial RegWrite() failed") 42 | } 43 | } 44 | 45 | // check first set 46 | for i, e := range enums { 47 | if val, err := regs.RegRead(e); err != nil { 48 | t.Fatal(err, "initial RegRead() failed") 49 | } else if val != uint64(i*2) { 50 | t.Fatalf("RegRead() returned %d, expecting %d", val, i*2) 51 | } 52 | } 53 | 54 | // restore context and check 55 | if err := regs.ContextRestore(ctx); err != nil { 56 | t.Fatal(err, "ContextRestore() failed") 57 | } 58 | for _, e := range enums { 59 | if val, err := regs.RegRead(e); err != nil { 60 | t.Fatal(err, "RegRead() failed") 61 | } else if val != 0 { 62 | t.Fatalf("RegRead() returned %d, expecting 0", val) 63 | } 64 | } 65 | 66 | // test reusing context 67 | if err := regs.RegWrite(enums[0], 1); err != nil { 68 | t.Fatal(err, "RegWrite() failed") 69 | } 70 | if _, err := regs.ContextSave(ctx); err != nil { 71 | t.Fatal(err, "ContextSave() failed") 72 | } 73 | if err := regs.RegWrite(enums[0], 0); err != nil { 74 | t.Fatal(err, "RegWrite() failed") 75 | } 76 | if err := regs.ContextRestore(ctx); err != nil { 77 | t.Fatal(err, "ContextRestore() failed") 78 | } 79 | if val, err := regs.RegRead(enums[0]); err != nil { 80 | t.Fatal(err, "RegRead() failed") 81 | } else if val != 1 { 82 | t.Fatalf("RegRead() returned %d, expecting 1", val) 83 | } 84 | } 85 | 86 | func TestRegs8(t *testing.T) { 87 | enums, regs := makeRegs(8) 88 | if err := regs.RegWrite(enums[0], 0xffff); err != nil { 89 | t.Fatal("RegWrite() failed") 90 | } 91 | if val, err := regs.RegRead(enums[0]); err != nil { 92 | t.Fatal("RegRead() failed") 93 | } else if val != 0xffff&0xff { 94 | t.Fatalf("RegRead() returned %d, expecting 255", val) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /go/models/trace/ops_test.go: -------------------------------------------------------------------------------- 1 | package trace 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "testing" 7 | 8 | "github.com/lunixbochs/usercorn/go/models" 9 | ) 10 | 11 | // these OPs are ordered to be semi-valid, so not by number 12 | var allButSyscall = []models.Op{ 13 | &OpNop{}, 14 | &OpMemMap{0x1000, 0x1000, 7, 0, 0, "", ""}, 15 | &OpMemMap{0x1000, 0x1000, 7, 1234, 0, "desc", "filename"}, 16 | &OpMemProt{0x1000, 0x1000, 0}, 17 | &OpMemProt{0x1000, 0x1000, 7}, 18 | &OpMemWrite{0x1000, []byte{0x48, 0xc7, 0xc0, 0x01, 0x00, 0x00, 0x00}}, // mov rax, 1 19 | &OpJmp{0x1000, 0x7}, 20 | &OpStep{0x7}, 21 | &OpReg{35, 1}, // 35 is rax 22 | // no SpReg support higher in the stack yet, so this is bogus 23 | &OpSpReg{0xff, []byte{1, 2, 3, 4}}, 24 | &OpMemRead{0x1000, 1}, 25 | &OpMemUnmap{0x1000, 0x1000}, 26 | &OpExit{}, 27 | } 28 | 29 | var testSyscall = &OpSyscall{ 30 | Num: 1, 31 | Ret: 2, 32 | Args: []uint64{1, 2, 3, 4, 5, 6, 7, 8}, 33 | Ops: allButSyscall, 34 | } 35 | 36 | var allUnframed = append(allButSyscall, testSyscall) 37 | 38 | var testFrame = &OpFrame{Ops: allUnframed} 39 | var testKeyframe = &OpKeyframe{Ops: allUnframed} 40 | 41 | func TestOpFrame(t *testing.T) { 42 | buf := make([]byte, testFrame.Sizeof()) 43 | testFrame.Pack(buf) 44 | op, _, err := Unpack(bytes.NewReader(buf), false) 45 | if err != nil { 46 | t.Fatal(err) 47 | } 48 | 49 | buf2 := make([]byte, op.Sizeof()) 50 | op.Pack(buf2) 51 | _, _, err = Unpack(bytes.NewReader(buf2), false) 52 | if err != nil { 53 | t.Fatal(err) 54 | } 55 | if !bytes.Equal(buf, buf2) { 56 | t.Error("encoded forms differ") 57 | } 58 | } 59 | 60 | func BenchmarkPack(b *testing.B) { 61 | for i := 0; i < b.N; i++ { 62 | tmp := make([]byte, testFrame.Sizeof()) 63 | testFrame.Pack(tmp) 64 | } 65 | } 66 | 67 | func BenchmarkUnpack(b *testing.B) { 68 | tmp := make([]byte, testFrame.Sizeof()) 69 | testFrame.Pack(tmp) 70 | r := bytes.NewReader(tmp) 71 | b.ResetTimer() 72 | for i := 0; i < b.N; i++ { 73 | r.Seek(0, 0) 74 | if _, _, err := Unpack(r, false); err != nil { 75 | b.Fatal(err) 76 | } 77 | } 78 | } 79 | 80 | func BenchmarkJsonPack(b *testing.B) { 81 | _, err := json.Marshal(testFrame) 82 | if err != nil { 83 | b.Fatal(err) 84 | } 85 | b.ResetTimer() 86 | for i := 0; i < b.N; i++ { 87 | testFrame.MarshalJSON() 88 | } 89 | } 90 | 91 | func BenchmarkJsonUnpack(b *testing.B) { 92 | s, err := json.Marshal(testFrame) 93 | if err != nil { 94 | b.Fatal(err) 95 | } 96 | b.ResetTimer() 97 | for i := 0; i < b.N; i++ { 98 | dict := make(map[string]interface{}) 99 | if err := json.Unmarshal(s, &dict); err != nil { 100 | b.Fatal(err) 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /go/kernel/common/kernel.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "github.com/lunixbochs/argjoy" 5 | "reflect" 6 | "strings" 7 | "unicode" 8 | "unicode/utf8" 9 | 10 | "github.com/lunixbochs/usercorn/go/models" 11 | ) 12 | 13 | type KernelBase struct { 14 | Syscalls map[string]Syscall 15 | U models.Usercorn 16 | Argjoy argjoy.Argjoy 17 | Pack func(b Buf, i interface{}) error 18 | } 19 | 20 | func (k *KernelBase) UsercornKernel() *KernelBase { 21 | return k 22 | } 23 | 24 | type Kernel interface { 25 | UsercornKernel() *KernelBase 26 | } 27 | 28 | func camelToSnakeCase(name string) string { 29 | var words []string 30 | last := 0 31 | for i, c := range name { 32 | if unicode.IsUpper(c) { 33 | if i > 0 { 34 | words = append(words, name[last:i]) 35 | } 36 | last = i 37 | } 38 | } 39 | words = append(words, name[last:]) 40 | return strings.ToLower(strings.Join(words, "_")) 41 | } 42 | 43 | func initKernel(kf Kernel) { 44 | k := kf.UsercornKernel() 45 | k.Syscalls = make(map[string]Syscall) 46 | instance := reflect.ValueOf(kf) 47 | typ := instance.Type() 48 | for i := 0; i < typ.NumMethod(); i++ { 49 | method := typ.Method(i) 50 | name := method.Name 51 | if strings.HasPrefix(name, "Literal") { 52 | name = strings.Replace(name, "Literal", "", 1) 53 | } else if r, size := utf8.DecodeRuneInString(name); size <= 0 || !unicode.IsUpper(r) { 54 | // skip private or broken unicode methods 55 | continue 56 | } 57 | name = camelToSnakeCase(name) 58 | in := make([]reflect.Type, method.Type.NumIn()-1) 59 | for j := 1; j < method.Type.NumIn(); j++ { 60 | in[j-1] = method.Type.In(j) 61 | } 62 | obufArr := false 63 | uintArr := false 64 | if len(in) > 0 { 65 | if in[0] == reflect.SliceOf(reflect.TypeOf(Obuf{})) { 66 | obufArr = true 67 | } else if in[0] == reflect.SliceOf(reflect.TypeOf(uint64(0))) { 68 | uintArr = true 69 | } 70 | } 71 | if obufArr || uintArr { 72 | in = in[1:] 73 | } 74 | out := make([]reflect.Type, method.Type.NumOut()) 75 | for j := 0; j < method.Type.NumOut(); j++ { 76 | out[j] = method.Type.Out(j) 77 | } 78 | k.Syscalls[name] = Syscall{ 79 | Name: name, 80 | Kernel: k, 81 | Instance: instance, 82 | Method: method, 83 | In: in, Out: out, 84 | ObufArr: obufArr, 85 | } 86 | } 87 | k.Argjoy.Register(k.commonArgCodec) 88 | k.Argjoy.Register(argjoy.IntToInt) 89 | } 90 | 91 | func Lookup(u models.Usercorn, kf Kernel, name string) *Syscall { 92 | k := kf.UsercornKernel() 93 | k.U = u 94 | if k.Syscalls == nil { 95 | initKernel(kf) 96 | } 97 | if sys, ok := k.Syscalls[name]; ok { 98 | return &sys 99 | } 100 | return nil 101 | } 102 | -------------------------------------------------------------------------------- /go/kernel/posix/kernel.go: -------------------------------------------------------------------------------- 1 | package posix 2 | 3 | import ( 4 | "os" 5 | 6 | co "github.com/lunixbochs/usercorn/go/kernel/common" 7 | "github.com/lunixbochs/usercorn/go/models" 8 | ) 9 | 10 | type PosixKernel struct { 11 | *co.KernelBase 12 | Unpack func(co.Buf, interface{}) 13 | Files map[co.Fd]*File 14 | } 15 | 16 | type File struct { 17 | Fd co.Fd 18 | Path string 19 | Mode int 20 | Flags int 21 | // entries/offset currently only used by Linux getdents 22 | Dirents []os.FileInfo 23 | Offset uint64 24 | } 25 | 26 | func NewKernel() *PosixKernel { 27 | return &PosixKernel{ 28 | KernelBase: &co.KernelBase{}, 29 | Files: make(map[co.Fd]*File), 30 | } 31 | } 32 | 33 | func PackAddrs(u models.Usercorn, addrs []uint64) ([]byte, error) { 34 | buf := make([]byte, int(u.Bits())/8*(len(addrs)+1)) 35 | pos := buf 36 | for _, v := range addrs { 37 | x, err := u.PackAddr(pos, v) 38 | if err != nil { 39 | return nil, err 40 | } 41 | pos = pos[len(x):] 42 | } 43 | return buf, nil 44 | } 45 | 46 | func PushStrings(u models.Usercorn, args ...string) ([]uint64, error) { 47 | addrs := make([]uint64, 0, len(args)+1) 48 | for _, arg := range args { 49 | if addr, err := u.PushBytes([]byte(arg + "\x00")); err != nil { 50 | return nil, err 51 | } else { 52 | addrs = append(addrs, addr) 53 | } 54 | } 55 | return addrs, nil 56 | } 57 | 58 | func StackInit(u models.Usercorn, args, env []string, auxv []byte) error { 59 | if _, err := u.Push(0); err != nil { 60 | return err 61 | } 62 | if len(args) > 0 { 63 | if _, err := u.PushBytes([]byte(args[0] + "\x00")); err != nil { 64 | return err 65 | } 66 | } 67 | // push argv and envp strings 68 | envp, err := PushStrings(u, env...) 69 | if err != nil { 70 | return err 71 | } 72 | argv, err := PushStrings(u, args...) 73 | if err != nil { 74 | return err 75 | } 76 | // precalc envp -> argc for stack alignment 77 | envpb, err := PackAddrs(u, envp) 78 | if err != nil { 79 | return err 80 | } 81 | argvb, err := PackAddrs(u, argv) 82 | if err != nil { 83 | return err 84 | } 85 | var tmp [8]byte 86 | argcb, err := u.PackAddr(tmp[:], uint64(len(argv))) 87 | if err != nil { 88 | return err 89 | } 90 | init := append(argcb, argvb...) 91 | init = append(init, envpb...) 92 | // align stack pointer 93 | sp, _ := u.RegRead(u.Arch().SP) 94 | sp &= ^uint64(15) 95 | off := len(init) & 15 96 | if off > 0 { 97 | sp -= uint64(16 - off) 98 | } 99 | if err := u.RegWrite(u.Arch().SP, sp); err != nil { 100 | return err 101 | } 102 | // auxv 103 | if len(auxv) > 0 { 104 | if _, err := u.PushBytes(auxv); err != nil { 105 | return err 106 | } 107 | } 108 | // write envp -> argc 109 | _, err = u.PushBytes(init) 110 | return err 111 | } 112 | -------------------------------------------------------------------------------- /go/cpu/unicorn/unicorn.go: -------------------------------------------------------------------------------- 1 | package unicorn 2 | 3 | import ( 4 | "github.com/pkg/errors" 5 | uc "github.com/unicorn-engine/unicorn/bindings/go/unicorn" 6 | 7 | "github.com/lunixbochs/usercorn/go/models/cpu" 8 | ) 9 | 10 | type Builder struct { 11 | Arch, Mode int 12 | } 13 | 14 | func (b *Builder) New() (cpu.Cpu, error) { 15 | u, err := uc.NewUnicorn(b.Arch, b.Mode) 16 | if err != nil { 17 | return nil, errors.Wrap(err, "NewUnicorn() failed") 18 | } 19 | return &UnicornCpu{u}, nil 20 | } 21 | 22 | type UnicornCpu struct { 23 | uc.Unicorn 24 | } 25 | 26 | func (u *UnicornCpu) Backend() interface{} { 27 | return u.Unicorn 28 | } 29 | 30 | func (u *UnicornCpu) ContextSave(reuse interface{}) (interface{}, error) { 31 | if reuse == nil { 32 | return u.Unicorn.ContextSave(nil) 33 | } 34 | return u.Unicorn.ContextSave(reuse.(uc.Context)) 35 | } 36 | 37 | func (u *UnicornCpu) ContextRestore(ctx interface{}) error { 38 | return u.Unicorn.ContextRestore(ctx.(uc.Context)) 39 | } 40 | 41 | func (u *UnicornCpu) HookAdd(htype int, cb interface{}, start uint64, end uint64, extra ...int) (cpu.Hook, error) { 42 | // have to wrap all hooks to conform to Cpu interface :( 43 | // if I fork Unicorn bindings I can remove the cpu arg to make this easier 44 | var wrap interface{} 45 | switch htype { 46 | case cpu.HOOK_BLOCK, cpu.HOOK_CODE: 47 | cbc := cb.(func(cpu.Cpu, uint64, uint32)) 48 | wrap = func(_ uc.Unicorn, addr uint64, size uint32) { cbc(u, addr, size) } 49 | 50 | case cpu.HOOK_MEM_READ, cpu.HOOK_MEM_WRITE, cpu.HOOK_MEM_READ | cpu.HOOK_MEM_WRITE: 51 | cbc := cb.(func(cpu.Cpu, int, uint64, int, int64)) 52 | wrap = func(_ uc.Unicorn, access int, addr uint64, size int, val int64) { cbc(u, access, addr, size, val) } 53 | 54 | case cpu.HOOK_INTR: 55 | cbc := cb.(func(cpu.Cpu, uint32)) 56 | wrap = func(_ uc.Unicorn, intno uint32) { cbc(u, intno) } 57 | 58 | case cpu.HOOK_INSN: 59 | // don't need to wrap HOOK_INSN because only arch-aware callers will use it 60 | wrap = cb 61 | 62 | default: 63 | // special case for mask 64 | if htype&(uc.HOOK_MEM_READ_UNMAPPED|uc.HOOK_MEM_WRITE_UNMAPPED|uc.HOOK_MEM_FETCH_UNMAPPED| 65 | uc.HOOK_MEM_READ_PROT|uc.HOOK_MEM_WRITE_PROT|uc.HOOK_MEM_FETCH_PROT) != 0 { 66 | 67 | cbc := cb.(func(cpu.Cpu, int, uint64, int, int64) bool) 68 | wrap = func(_ uc.Unicorn, access int, addr uint64, size int, val int64) bool { 69 | return cbc(u, access, addr, size, val) 70 | } 71 | } else { 72 | return 0, errors.New("Unknown hook type.") 73 | } 74 | } 75 | return u.Unicorn.HookAdd(htype, wrap, start, end, extra...) 76 | } 77 | 78 | func (u *UnicornCpu) HookDel(hh cpu.Hook) error { 79 | return u.Unicorn.HookDel(hh.(uc.Hook)) 80 | } 81 | 82 | func (u *UnicornCpu) MemMap(addr, size uint64, prot int) error { 83 | return u.Unicorn.MemMapProt(addr, size, prot) 84 | } 85 | 86 | func (u *UnicornCpu) MemProt(addr, size uint64, prot int) error { 87 | return u.Unicorn.MemProtect(addr, size, prot) 88 | } 89 | -------------------------------------------------------------------------------- /go/cmd/imgtrace/imgtrace.go: -------------------------------------------------------------------------------- 1 | package imgtrace 2 | 3 | import ( 4 | "fmt" 5 | "image/jpeg" 6 | "os" 7 | "path" 8 | 9 | "github.com/lunixbochs/usercorn/go/cmd" 10 | "github.com/lunixbochs/usercorn/go/models/cpu" 11 | ) 12 | 13 | func Main(args []string) { 14 | c := cmd.NewUsercornCmd() 15 | var width *int 16 | var frameskip *int 17 | var outdir *string 18 | var binimg *bool 19 | var render *string 20 | 21 | var memImg *memImage 22 | jpegOptions := &jpeg.Options{Quality: 90} 23 | 24 | frame := 0 25 | skip := 0 26 | memTrace := func(u cpu.Cpu, access int, addr uint64, size int, value int64) { 27 | var buf [8]byte 28 | b, _ := c.Usercorn.PackAddr(buf[:], uint64(value)) 29 | memImg.traceMem(c.Usercorn, addr, b[:size]) 30 | if value != 0 { 31 | memImg.dirty = true 32 | } 33 | } 34 | blockTrace := func(u cpu.Cpu, addr uint64, size uint32) { 35 | if !memImg.dirty { 36 | return 37 | } 38 | memImg.dirty = false 39 | 40 | if *frameskip > 0 { 41 | if skip < *frameskip { 42 | skip++ 43 | return 44 | } 45 | skip = 0 46 | } 47 | frame++ 48 | file, err := os.Create(path.Join(*outdir, fmt.Sprintf("%d.jpg", frame))) 49 | if err != nil { 50 | panic(err) 51 | } 52 | if err := jpeg.Encode(file, memImg.render(), jpegOptions); err != nil { 53 | panic(err) 54 | } 55 | file.Close() 56 | } 57 | c.SetupFlags = func() error { 58 | width = c.Flags.Int("size", 8, "block width (blocks are square)") 59 | outdir = c.Flags.String("out", "out/", "image output directory") 60 | binimg = c.Flags.Bool("binimg", false, "include binary+interpreter in trace") 61 | frameskip = c.Flags.Int("frameskip", 0, "only record every N image frames") 62 | render = c.Flags.String("render", "linear", "render type {linear, block, hilbert, digram}") 63 | return nil 64 | } 65 | c.SetupUsercorn = func() error { 66 | var rtype int 67 | switch *render { 68 | case "linear": 69 | rtype = RENDER_LINEAR 70 | case "block": 71 | rtype = RENDER_BLOCK 72 | case "hilbert": 73 | rtype = RENDER_HILBERT 74 | case "digram": 75 | rtype = RENDER_DIGRAM 76 | default: 77 | return fmt.Errorf("unknown render type: %s", *render) 78 | } 79 | memImg = NewMemImage(*width, *width, rtype) 80 | if err := os.Mkdir(*outdir, 0755); err != nil { 81 | return err 82 | } 83 | if _, err := c.Usercorn.HookAdd(cpu.HOOK_MEM_WRITE, memTrace, 1, 0); err != nil { 84 | return err 85 | } 86 | if _, err := c.Usercorn.HookAdd(cpu.HOOK_BLOCK, blockTrace, 1, 0); err != nil { 87 | return err 88 | } 89 | if *binimg { 90 | // pre-fill memory with binary 91 | for _, m := range c.Usercorn.Mappings() { 92 | mem, err := c.Usercorn.MemRead(m.Addr, m.Size) 93 | if err == nil { 94 | memImg.traceMem(c.Usercorn, m.Addr, mem) 95 | } 96 | } 97 | } 98 | return nil 99 | } 100 | os.Exit(c.Run(args, os.Environ())) 101 | } 102 | 103 | func init() { cmd.Register("imgtrace", "record memory access patterns to image files", Main) } 104 | -------------------------------------------------------------------------------- /go/kernel/linux/elf_auxv.go: -------------------------------------------------------------------------------- 1 | package linux 2 | 3 | import ( 4 | "bytes" 5 | "crypto/rand" 6 | "github.com/lunixbochs/struc" 7 | "os" 8 | 9 | "github.com/lunixbochs/usercorn/go/models" 10 | ) 11 | 12 | const ( 13 | ELF_AT_NULL = iota 14 | ELF_AT_IGNORE 15 | ELF_AT_EXECFD 16 | ELF_AT_PHDR 17 | ELF_AT_PHENT 18 | ELF_AT_PHNUM 19 | ELF_AT_PAGESZ 20 | ELF_AT_BASE 21 | ELF_AT_FLAGS 22 | ELF_AT_ENTRY 23 | ELF_AT_NOTELF 24 | ELF_AT_UID 25 | ELF_AT_EUID 26 | ELF_AT_GID 27 | ELF_AT_EGID 28 | ELF_AT_PLATFORM 29 | ELF_AT_HWCAP 30 | ELF_AT_CLKTCK = 17 31 | ELF_AT_SECURE = 23 32 | ELF_AT_RANDOM = 25 33 | ELF_AT_SYSINFO = 32 34 | ELF_AT_SYSINFO_EHDR = 33 35 | ) 36 | 37 | type ElfAuxv struct { 38 | Type, Val uint64 `struc:"size_t"` 39 | } 40 | 41 | func add(auxv []ElfAuxv, t, val uint64) []ElfAuxv { 42 | return append(auxv, ElfAuxv{t, val}) 43 | } 44 | 45 | func setupElfAuxv(u models.Usercorn) ([]ElfAuxv, error) { 46 | // set up AT_RANDOM 47 | var tmp [16]byte 48 | if _, err := rand.Read(tmp[:]); err != nil { 49 | return nil, err 50 | } 51 | randAddr, err := u.PushBytes(tmp[:]) 52 | if err != nil { 53 | return nil, err 54 | } 55 | // insert platform string 56 | platformAddr, err := u.PushBytes([]byte(u.Loader().Arch() + "\x00")) 57 | if err != nil { 58 | return nil, err 59 | } 60 | // main auxv table 61 | auxv := []ElfAuxv{ 62 | // TODO: set/track a page size somewhere - on Arch.OS? 63 | {ELF_AT_PAGESZ, 4096}, 64 | {ELF_AT_BASE, u.InterpBase()}, 65 | {ELF_AT_FLAGS, 0}, 66 | {ELF_AT_ENTRY, uint64(u.BinEntry())}, 67 | {ELF_AT_UID, uint64(os.Getuid())}, 68 | {ELF_AT_EUID, uint64(os.Geteuid())}, 69 | {ELF_AT_GID, uint64(os.Getgid())}, 70 | {ELF_AT_EGID, uint64(os.Getegid())}, 71 | {ELF_AT_PLATFORM, platformAddr}, 72 | {ELF_AT_CLKTCK, 100}, // 100hz, totally fake 73 | {ELF_AT_SECURE, 0}, // TODO: (getuid() != geteuid() || getgid() != getegid()) 74 | {ELF_AT_RANDOM, randAddr}, 75 | {ELF_AT_NULL, 0}, 76 | } 77 | // add phdr information if present in binary 78 | phdrOff, _, phdrCount := u.Loader().Header() 79 | segments, _ := u.Loader().Segments() 80 | for _, s := range segments { 81 | if s.ContainsPhys(phdrOff) { 82 | phdrOff += s.Addr 83 | break 84 | } 85 | } 86 | phdrEnt := 56 87 | if u.Bits() == 32 { 88 | phdrEnt = 32 89 | } 90 | if phdrOff > 0 { 91 | auxv = append([]ElfAuxv{ 92 | {ELF_AT_PHDR, u.Base() + phdrOff}, 93 | {ELF_AT_PHENT, uint64(phdrEnt)}, 94 | {ELF_AT_PHNUM, uint64(phdrCount)}, 95 | }, auxv...) 96 | } 97 | return auxv, nil 98 | } 99 | 100 | func SetupElfAuxv(u models.Usercorn) ([]byte, error) { 101 | var buf bytes.Buffer 102 | auxv, err := setupElfAuxv(u) 103 | if err != nil { 104 | return nil, err 105 | } 106 | options := &struc.Options{ 107 | PtrSize: int(u.Bits()), 108 | Order: u.ByteOrder(), 109 | } 110 | for _, a := range auxv { 111 | if err := struc.PackWithOptions(&buf, &a, options); err != nil { 112 | return nil, err 113 | } 114 | } 115 | return buf.Bytes(), err 116 | } 117 | -------------------------------------------------------------------------------- /go/models/loopdetect.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "strconv" 5 | "strings" 6 | ) 7 | 8 | type Loop struct { 9 | Loop []uint64 10 | Index, Filled int 11 | } 12 | 13 | func NewLoop(length int) *Loop { 14 | return &Loop{make([]uint64, length), 0, 0} 15 | } 16 | 17 | func (l *Loop) Inc() { 18 | if l.Index > l.Filled { 19 | l.Filled = l.Index 20 | } 21 | l.Index = (l.Index + 1) % len(l.Loop) 22 | } 23 | 24 | func (l *Loop) Push(n uint64) { 25 | l.Loop[l.Index] = n 26 | l.Inc() 27 | } 28 | 29 | func (l *Loop) Next() uint64 { 30 | n := l.Loop[l.Index] 31 | l.Inc() 32 | return n 33 | } 34 | 35 | func (l *Loop) Ring(start int, dst []uint64) []uint64 { 36 | i := 0 37 | for i < len(dst) { 38 | idx := (l.Index - 1 + start + i) % len(l.Loop) 39 | for idx < 0 { 40 | idx += len(l.Loop) 41 | } 42 | if idx > l.Filled { 43 | dst = dst[:len(dst)-1] 44 | } else { 45 | dst[i] = l.Loop[idx] 46 | i++ 47 | } 48 | } 49 | return dst 50 | } 51 | 52 | type LoopDetect struct { 53 | History, Loop *Loop 54 | Loops, Len int 55 | } 56 | 57 | func NewLoopDetect(length int) *LoopDetect { 58 | return &LoopDetect{ 59 | History: NewLoop(length * 2), 60 | Len: length, 61 | } 62 | } 63 | 64 | func (l *LoopDetect) Update(addr uint64) (bool, []uint64, int) { 65 | if l.Loop != nil { 66 | loop := l.Loop 67 | idx := loop.Index 68 | next := loop.Next() 69 | if next == addr { 70 | if idx == 0 { 71 | l.Loops++ 72 | } 73 | return true, loop.Loop, l.Loops 74 | } else { 75 | loops := l.Loops 76 | l.Loop = nil 77 | l.Loops = 0 78 | return false, loop.Loop, loops 79 | } 80 | } 81 | l.History.Push(addr) 82 | loop := l.Detect() 83 | if loop != nil { 84 | l.Loop = loop 85 | l.Loops = 1 86 | return true, loop.Loop, l.Loops 87 | } 88 | return false, nil, 0 89 | } 90 | 91 | func (l *LoopDetect) Detect() *Loop { 92 | equals := func(a, b []uint64, length int) bool { 93 | if len(a) != len(b) || len(a) == 0 || len(a) != length { 94 | return false 95 | } 96 | for i, v := range a { 97 | if b[i] != v { 98 | return false 99 | } 100 | } 101 | return true 102 | } 103 | // TODO: stack allocate if len is short? 104 | // we need to return test[:n] so it only works for cmp 105 | test := make([]uint64, l.Len) 106 | cmp := make([]uint64, l.Len) 107 | min := l.Len 108 | alt := (l.History.Filled + 1) / 2 109 | if alt < min { 110 | min = alt 111 | } 112 | for n := 1; n <= min; n++ { 113 | loop := l.History.Ring(-n+1, test[:n]) 114 | cmp := l.History.Ring(-n*2+1, cmp[:n]) 115 | if equals(loop, cmp, n) { 116 | return &Loop{loop, 0, 0} 117 | } 118 | } 119 | return nil 120 | } 121 | 122 | func (l *LoopDetect) String(loop []uint64) string { 123 | tmp := make([]string, len(loop)) 124 | for i, v := range loop { 125 | tmp[i] = strconv.FormatUint(v, 16) 126 | } 127 | return "[" + strings.Join(tmp, ", ") + "]" 128 | } 129 | 130 | func (l *LoopDetect) Reset() { 131 | l.Loop = nil 132 | l.Loops = 0 133 | l.History.Index = 0 134 | l.History.Filled = 0 135 | } 136 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | usercorn 2 | ---- 3 | 4 | [![Build Status](https://travis-ci.org/lunixbochs/usercorn.svg?branch=master)](https://travis-ci.org/lunixbochs/usercorn) 5 | [![GoDoc](https://godoc.org/github.com/lunixbochs/usercorn?status.svg)](https://godoc.org/github.com/lunixbochs/usercorn) 6 | [![Slack](https://lunixbochs.herokuapp.com/badge.svg)](https://lunixbochs.herokuapp.com/) 7 | 8 | Building 9 | --- 10 | 11 | Usercorn depends on Go 1.6 or newer, as well as the latest unstable versions of Capstone, Unicorn, and Keystone. 12 | 13 | `make deps` (requires `cmake`) will attempt to install all of the above dependencies into the source tree under `deps/`. 14 | 15 | `make` will update Go packages and build `usercorn` 16 | 17 | Example Commands 18 | --- 19 | 20 | usercorn run bins/x86.linux.elf 21 | usercorn run bins/x86_64.linux.elf 22 | usercorn run bins/x86.darwin.macho 23 | usercorn run bins/x86_64.darwin.macho 24 | usercorn run bins/x86.linux.cgc 25 | usercorn run bins/mipsel.linux.elf 26 | 27 | usercorn run -trace bins/x86.linux.elf 28 | usercorn run -trace -to trace.uc bins/x86.linux.elf 29 | usercorn trace -pretty trace.uc 30 | usercorn run -repl bins/x86.linux.elf 31 | 32 | What. 33 | ---- 34 | 35 | - Usercorn is an analysis and emulator framework, with a base similar to qemu-user. 36 | - It can run arbitrary binaries on a different host kernel, unlike qemu-user. 37 | - While recording full system state at every instruction. 38 | - to a serializable compact format capable of rewind and re-execution. 39 | - It's useful out of the box for debugging and dynamic analysis. 40 | - With an arch-neutral powerful lua-based scripting language and debugger. 41 | - It's also easy to extend and use to build your own tools. 42 | 43 | Usercorn could be used to emulate 16-bit DOS, 32-bit and 64-bit ARM/MIPS/x86/SPARC binaries for Linux, Darwin, BSD, DECREE, and even operating systems like Redux. 44 | 45 | Right now, x86\_64 linux and DECREE are the best supported guests. 46 | 47 | Why? 48 | ---- 49 | 50 | - Usercorn aims to be a framework to simplify emulating and deeply hooking a userspace environment for many target architectures and kernel ABIs. 51 | - Debug stubborn binaries. I had a binary gdb refused to debug ("Program exited during startup."). No problem. Usercorn can single-step into the program for you. 52 | - Debug foreign architecture and OS binaries. You don't need a MIPS box. You don't need qemu-user. You don't even need Linux. 53 | - Write tools, like fuzzers, static analyzers, recompilers, memory and register analysis, overlay code coverage and machine state into IDA/Binary Ninja. 54 | - Selectively call functions from within a binary. Usercorn will map a binary and emulate the kernel for you. 55 | - Whatever you want. Open an issue if you have a cool debugging / reverse engineering idea I didn't think about - I may just implement it. 56 | 57 | Caveats 58 | ---- 59 | 60 | - Your userspace might be incredibly confusing to the target binary. 61 | - No API for memory mapped files yet (kinda, if mmap() currently gets a file descriptor argument it will manually copy the file into memory). 62 | - I only have maybe 20% of the posix syscalls implemented, which is enough to run basic binaries. Busybox works great. 63 | 64 | [See Also](https://xkcd.com/1406/) (credit: XKCD) 65 | ---- 66 | ![Universal converter](https://imgs.xkcd.com/comics/universal_converter_box.png) 67 | -------------------------------------------------------------------------------- /go/cmd/trace/drcov.go: -------------------------------------------------------------------------------- 1 | package trace 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "fmt" 7 | "github.com/lunixbochs/struc" 8 | "github.com/pkg/errors" 9 | "io" 10 | "os" 11 | "sort" 12 | 13 | "github.com/lunixbochs/usercorn/go/arch" 14 | "github.com/lunixbochs/usercorn/go/models" 15 | "github.com/lunixbochs/usercorn/go/models/cpu" 16 | "github.com/lunixbochs/usercorn/go/models/debug" 17 | "github.com/lunixbochs/usercorn/go/models/trace" 18 | ) 19 | 20 | type drcovBB struct { 21 | Start uint32 22 | Size uint16 23 | ModId uint16 24 | } 25 | 26 | var strucOptions = &struc.Options{Order: binary.LittleEndian} 27 | 28 | func WriteDrcov(tf *trace.TraceReader, out *os.File) error { 29 | arch, OS, err := arch.GetArch(tf.Header.Arch, tf.Header.OS) 30 | if err != nil { 31 | return errors.Wrap(err, "arch.GetArch() failed") 32 | } 33 | config := &models.Config{} 34 | config.Init() 35 | replay := trace.NewReplay(arch, OS, tf.Header.CodeOrder, debug.NewDebug(tf.Header.Arch, config)) 36 | 37 | var blocks bytes.Buffer 38 | bbCount := 0 39 | modserial := 0 40 | modules := make(map[string]*cpu.Page) 41 | modlookup := make(cpu.Pages, 0) 42 | 43 | addmod := func(o *trace.OpMemMap) { 44 | if o.Prot&cpu.PROT_EXEC == 0 { 45 | return 46 | } 47 | key := o.Desc + "|" + o.File 48 | if _, ok := modules[key]; !ok { 49 | desc := o.File 50 | if desc == "" { 51 | desc = "[" + o.Desc + "]" 52 | } 53 | mod := &cpu.Page{Addr: o.Addr, Size: o.Size, Prot: modserial, Desc: desc} 54 | modules[key] = mod 55 | modlookup = append(modlookup, mod) 56 | modserial++ 57 | } 58 | sort.Sort(modlookup) 59 | } 60 | 61 | for { 62 | op, err := tf.Next() 63 | if err == io.EOF { 64 | break 65 | } else if err != nil { 66 | fmt.Println(errors.Wrap(err, "error reading next trace operation")) 67 | break 68 | } 69 | switch frame := op.(type) { 70 | case *trace.OpKeyframe: 71 | for _, op := range frame.Ops { 72 | switch o := op.(type) { 73 | case *trace.OpMemMap: 74 | addmod(o) 75 | } 76 | } 77 | case *trace.OpFrame: 78 | for _, op := range frame.Ops { 79 | switch o := op.(type) { 80 | case *trace.OpJmp: 81 | mod := modlookup.Find(o.Addr) 82 | if mod != nil { 83 | bb := drcovBB{ 84 | Start: uint32(o.Addr - mod.Addr), 85 | Size: uint16(o.Size), 86 | ModId: uint16(mod.Prot), 87 | } 88 | struc.PackWithOptions(&blocks, &bb, strucOptions) 89 | bbCount++ 90 | } 91 | case *trace.OpMemMap: 92 | addmod(o) 93 | case *trace.OpSyscall: 94 | for _, op := range o.Ops { 95 | switch o := op.(type) { 96 | case *trace.OpMemMap: 97 | addmod(o) 98 | } 99 | } 100 | } 101 | } 102 | } 103 | } 104 | replay.Flush() 105 | 106 | // write output file 107 | fmt.Fprintf(out, "DRCOV VERSION: 2\n") 108 | fmt.Fprintf(out, "DRCOV FLAVOR: drcov-64\n") 109 | fmt.Fprintf(out, "Module Table: version 2, count %d\n", len(modules)) 110 | fmt.Fprintf(out, "Columns: id, base, end, entry, path\n") 111 | for _, page := range modules { 112 | fmt.Fprintf(out, "%d, %#016x, %#016x, %#016x, %s\n", page.Prot, page.Addr, page.Addr+page.Size, 0, page.Desc) 113 | } 114 | fmt.Fprintf(out, "BB Table: %d bbs\n", bbCount) 115 | if _, err := blocks.WriteTo(out); err != nil { 116 | return err 117 | } 118 | return nil 119 | } 120 | -------------------------------------------------------------------------------- /go/models/debug/debug.go: -------------------------------------------------------------------------------- 1 | package debug 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path" 7 | "path/filepath" 8 | "sync" 9 | 10 | "github.com/lunixbochs/usercorn/go/loader" 11 | "github.com/lunixbochs/usercorn/go/models" 12 | "github.com/lunixbochs/usercorn/go/models/cpu" 13 | ) 14 | 15 | type Debug struct { 16 | sync.RWMutex 17 | arch string 18 | config *models.Config 19 | // map of absolute filesystem path to DebugFile 20 | files map[string]*DebugFile 21 | } 22 | 23 | func NewDebug(arch string, config *models.Config) *Debug { 24 | return &Debug{ 25 | arch: arch, 26 | config: config, 27 | files: make(map[string]*DebugFile), 28 | } 29 | } 30 | 31 | func (d *Debug) getFile(name string) (*DebugFile, error) { 32 | tmp, err := filepath.Abs(name) 33 | if err == nil { 34 | name = tmp 35 | } 36 | d.RLock() 37 | if df, ok := d.files[name]; ok { 38 | d.RUnlock() 39 | return df, nil 40 | } 41 | d.RUnlock() 42 | l, err := loader.LoadFileArch(name, d.arch) 43 | if err != nil { 44 | return nil, err 45 | } 46 | // TODO: how to surface a non-critical error? u.Log()? 47 | symbols, _ := l.Symbols() 48 | DWARF, _ := l.DWARF() 49 | df := &DebugFile{ 50 | Symbols: symbols, 51 | DWARF: DWARF, 52 | } 53 | df.CacheSym() 54 | d.Lock() 55 | d.files[name] = df 56 | d.Unlock() 57 | return df, nil 58 | } 59 | 60 | func (d *Debug) File(name string) (*DebugFile, error) { 61 | // TODO: symlinks probably need to be evaluated in terms of the prefix 62 | tmp, err := filepath.EvalSymlinks(name) 63 | if err == nil { 64 | name = tmp 65 | } 66 | // GNU/Linux: try loading /usr/lib/debug version of libraries for debug symbols 67 | // TODO: only do this on absolute paths? 68 | debugPath := filepath.Join("/usr/lib/debug", d.config.PrefixRel(name)) 69 | debugPath = d.config.PrefixPath(debugPath, false) 70 | if _, err := os.Stat(debugPath); err == nil { 71 | // try a match here 72 | df, err := d.getFile(debugPath) 73 | if err == nil { 74 | return df, nil 75 | } 76 | } 77 | // if no debug library was found, fall back to the original library 78 | return d.getFile(name) 79 | } 80 | 81 | func (d *Debug) Symbolicate(addr uint64, mem cpu.Pages, includeSource bool) (*models.Symbol, string) { 82 | var ( 83 | filename string 84 | fileLine string 85 | sym models.Symbol 86 | dist uint64 87 | ) 88 | page := mem.Find(addr) 89 | if page != nil && page.File != nil { 90 | filename = path.Base(page.File.Name) 91 | df, _ := d.File(page.File.Name) 92 | if df != nil { 93 | sym, dist = df.Symbolicate(addr - page.Addr + page.File.Off) 94 | if sym.Name == "" { 95 | // FIXME: we should know if this is PIE or not and adjust how we look up symbols instead of trying both ways 96 | sym, dist = df.Symbolicate(addr) 97 | } 98 | if sym.Name != "" && includeSource { 99 | if fl := df.FileLine(addr); fl != nil { 100 | fileLine = fmt.Sprintf("%s:%d", path.Base(fl.File.Name), fl.Line) 101 | } 102 | } 103 | } 104 | } 105 | name := sym.Name 106 | if name != "" { 107 | if d.config.Demangle { 108 | name = models.Demangle(name) 109 | } 110 | if d.config.SymFile && filename != "" { 111 | name = fmt.Sprintf("%s@%s", name, filename) 112 | } 113 | if dist > 0 { 114 | name = fmt.Sprintf("%s+0x%x", name, dist) 115 | } 116 | if fileLine != "" { 117 | name = fmt.Sprintf("%s (%s)", name, fileLine) 118 | } 119 | } 120 | return &sym, name 121 | } 122 | -------------------------------------------------------------------------------- /go/cmd/repl/repl.go: -------------------------------------------------------------------------------- 1 | package repl 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | "strconv" 8 | "strings" 9 | 10 | "github.com/lunixbochs/usercorn/go/cmd" 11 | "github.com/lunixbochs/usercorn/go/models" 12 | "github.com/lunixbochs/usercorn/go/models/cpu" 13 | ) 14 | 15 | var helpTxt = ` 16 | Commands: 17 | .alloc 18 | .read [size=64] 19 | .write 20 | .regs 21 | ` 22 | 23 | func parseAddr(u models.Usercorn, line string) uint64 { 24 | n, _ := strconv.ParseUint(line, 0, 64) 25 | return n 26 | } 27 | 28 | func handleCmd(c *cmd.UsercornCmd, line string) bool { 29 | u := c.Usercorn 30 | args := strings.Split(line, " ") 31 | 32 | switch args[0][1:] { 33 | case "help": 34 | fmt.Println(helpTxt) 35 | case "alloc": 36 | if len(args) != 2 { 37 | fmt.Println(helpTxt) 38 | } else { 39 | addr, err := u.Malloc(parseAddr(u, args[1]), "alloc") 40 | if err != nil { 41 | fmt.Println(err) 42 | } else { 43 | fmt.Printf(" = 0x%x\n", addr) 44 | } 45 | } 46 | case "read": 47 | if len(args) < 2 { 48 | fmt.Println(helpTxt) 49 | } else { 50 | addr := parseAddr(u, args[1]) 51 | size := uint64(64) 52 | if len(args) > 2 { 53 | size = parseAddr(u, args[2]) 54 | } 55 | mem, err := u.MemRead(addr, size) 56 | if err != nil { 57 | fmt.Println(err) 58 | break 59 | } 60 | for _, line := range models.HexDump(addr, mem, int(u.Bits())) { 61 | fmt.Printf(" %s\n", line) 62 | } 63 | } 64 | case "write": 65 | if len(args) < 3 { 66 | fmt.Println(helpTxt) 67 | } else { 68 | addr := parseAddr(u, args[1]) 69 | rest := strings.Join(args[2:], " ") 70 | rest, err := strconv.Unquote("\"" + rest + "\"") 71 | if err != nil { 72 | fmt.Println(err) 73 | break 74 | } 75 | err = u.MemWrite(addr, []byte(rest)) 76 | if err != nil { 77 | fmt.Println(err) 78 | break 79 | } 80 | } 81 | case "regs": 82 | status := models.StatusDiff{U: u} 83 | fmt.Printf("%s", status.Changes(false).String(c.Config.Color)) 84 | default: 85 | return false 86 | } 87 | return true 88 | } 89 | 90 | func Main(args []string) { 91 | c := cmd.NewUsercornRawCmd() 92 | c.NoExe = true 93 | c.NoArgs = true 94 | 95 | c.RunUsercorn = func() error { 96 | u := c.Usercorn 97 | addr, err := u.Mmap(u.Entry(), 0x10000, cpu.PROT_ALL, false, "repl", nil) 98 | if err != nil { 99 | return err 100 | } 101 | 102 | status := models.StatusDiff{U: u} 103 | fmt.Printf("%s", status.Changes(false).String(c.Config.Color)) 104 | end := addr 105 | input := bufio.NewScanner(os.Stdin) 106 | for { 107 | fmt.Printf("%s", status.Changes(true).String(c.Config.Color)) 108 | fmt.Printf("0x%x: ", addr) 109 | if !input.Scan() { 110 | break 111 | } 112 | text := input.Text() 113 | if len(text) > 0 && text[0] == '.' { 114 | if handleCmd(c, text) { 115 | continue 116 | } 117 | } 118 | sc, err := u.Asm(text, addr) 119 | if err != nil { 120 | fmt.Printf("asm err: %s\n", err) 121 | continue 122 | } 123 | if err := u.MemWrite(addr, sc); err != nil { 124 | fmt.Printf("write err: %s\n", err) 125 | continue 126 | } 127 | end = addr + uint64(len(sc)) 128 | u.SetEntry(addr) 129 | u.SetExit(end) 130 | if err := u.Run(); err != nil { 131 | fmt.Printf("exec err: %s\n", err) 132 | } 133 | addr = end 134 | } 135 | fmt.Printf("\n%s", status.Changes(false).String(c.Config.Color)) 136 | return nil 137 | } 138 | c.Run(args, os.Environ()) 139 | } 140 | 141 | func init() { cmd.Register("repl", "execute assembly on an interactive command line", Main) } 142 | -------------------------------------------------------------------------------- /go/ui/repl.go: -------------------------------------------------------------------------------- 1 | package ui 2 | 3 | import ( 4 | "fmt" 5 | "github.com/chzyer/readline" 6 | "github.com/shibukawa/configdir" 7 | "io" 8 | "os" 9 | "path/filepath" 10 | 11 | "github.com/lunixbochs/usercorn/go/lua" 12 | "github.com/lunixbochs/usercorn/go/models" 13 | ) 14 | 15 | type Repl struct { 16 | Lua *lua.LuaRepl 17 | u models.Usercorn 18 | rl *readline.Instance 19 | 20 | multiline bool 21 | lines []string 22 | Closed bool 23 | } 24 | 25 | type nullCloser struct{ io.Writer } 26 | 27 | func (n *nullCloser) Close() error { return nil } 28 | 29 | func NewRepl(u models.Usercorn) (*Repl, error) { 30 | // get history path 31 | configDirs := configdir.New("usercorn", "repl") 32 | cacheDir := configDirs.QueryCacheFolder() 33 | historyPath := "" 34 | if err := cacheDir.MkdirAll(); err == nil { 35 | historyPath = filepath.Join(cacheDir.Path, "history") 36 | } 37 | rl, err := readline.NewEx(&readline.Config{ 38 | InterruptPrompt: "\n", 39 | UniqueEditLine: false, 40 | HistoryFile: historyPath, 41 | }) 42 | if err != nil { 43 | return nil, err 44 | } 45 | luaRepl, err := lua.NewRepl(u, rl.Stderr()) 46 | if err != nil { 47 | rl.Close() 48 | return nil, err 49 | } 50 | // hijack usercorn output so we can reprint the prompt 51 | if u.Config().Output == os.Stderr { 52 | u.Config().Output = &nullCloser{rl.Stderr()} 53 | } else if a, ok := u.Config().Output.(*models.AsyncStream); ok { 54 | a.Move(&nullCloser{rl.Stderr()}) 55 | } 56 | return &Repl{u: u, Lua: luaRepl, rl: rl}, nil 57 | } 58 | 59 | func (r *Repl) Run() { 60 | go r.runSync() 61 | } 62 | 63 | func (r *Repl) OnChange(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool) { 64 | rl := r.rl 65 | if key == '\n' || key == '\r' && !r.multiline { 66 | rl.Config.UniqueEditLine = true 67 | } else if key > 0 { 68 | rl.Config.UniqueEditLine = false 69 | } 70 | // returning false keeps readline from messing up the prompt 71 | return line, pos, false 72 | } 73 | 74 | func (r *Repl) setPrompt() { 75 | u, rl := r.u, r.rl 76 | pc, _ := u.RegRead(u.Arch().PC) 77 | rl.SetPrompt(fmt.Sprintf("%#x> ", pc)) 78 | return 79 | // this is just a demo 80 | mem, _ := u.DirectRead(pc, 16) 81 | dis, err := u.Arch().Dis.Dis(mem, pc) 82 | if err == nil && len(dis) > 0 { 83 | r.rl.SetPrompt(fmt.Sprintf("[%s %s] ", dis[0].Mnemonic(), dis[0].OpStr())) 84 | } else { 85 | r.rl.SetPrompt("> ") 86 | } 87 | } 88 | 89 | func (r *Repl) Reset() { 90 | r.lines = nil 91 | r.multiline = false 92 | r.setPrompt() 93 | } 94 | 95 | func (r *Repl) runSync() { 96 | defer func() { 97 | r.u.Exit(models.ExitStatus(0)) 98 | r.u.Gate().Unlock() 99 | r.Close() 100 | }() 101 | 102 | r.rl.Config.Listener = r 103 | r.setPrompt() 104 | 105 | defer r.Close() 106 | for { 107 | ln := r.rl.Line() 108 | if ln.Error == readline.ErrInterrupt { 109 | r.setPrompt() 110 | r.multiline = false 111 | r.rl.Config.UniqueEditLine = false 112 | r.Reset() 113 | r.u.Stop() 114 | continue 115 | } else if ln.CanContinue() { 116 | continue 117 | } else if ln.CanBreak() { 118 | break 119 | } 120 | if !r.multiline { 121 | if ln.Line != "" { 122 | r.lines = []string{ln.Line} 123 | } 124 | } else { 125 | r.lines = append(r.lines, ln.Line) 126 | } 127 | if r.Lua.Exec(r.lines) { 128 | r.rl.Config.UniqueEditLine = false 129 | r.rl.SetPrompt("... ") 130 | r.multiline = true 131 | } else { 132 | r.multiline = false 133 | r.setPrompt() 134 | } 135 | } 136 | } 137 | 138 | func (r *Repl) Close() { 139 | r.Lua.Close() 140 | r.rl.Close() 141 | r.Closed = true 142 | } 143 | -------------------------------------------------------------------------------- /go/kernel/posix/stat_linux.go: -------------------------------------------------------------------------------- 1 | package posix 2 | 3 | // TODO: use FileInfo instead of nonportable Syscall interface? 4 | import ( 5 | "syscall" 6 | ) 7 | 8 | func NewLinuxStat_generic(stat *syscall.Stat_t, bits uint, large bool) interface{} { 9 | return &LinuxStat_generic{ 10 | Dev: uint32(stat.Dev), 11 | Ino: uint64(stat.Ino), 12 | Mode: uint32(stat.Mode), 13 | Uid: stat.Uid, 14 | Gid: stat.Gid, 15 | Rdev: uint32(stat.Rdev), 16 | Size: int64(stat.Size), 17 | Blksize: uint32(stat.Blksize), 18 | Blkcnt: uint64(stat.Blocks), 19 | Atime: uint32(stat.Atim.Sec), 20 | AtimeNsec: uint32(stat.Atim.Nsec), 21 | Mtime: uint32(stat.Mtim.Sec), 22 | MtimeNsec: uint32(stat.Mtim.Nsec), 23 | Ctime: uint32(stat.Ctim.Sec), 24 | CtimeNsec: uint32(stat.Ctim.Nsec), 25 | } 26 | } 27 | 28 | func NewLinuxStat_x86(stat *syscall.Stat_t, bits uint, large bool) interface{} { 29 | if bits == 64 { 30 | return &LinuxStat64_x86{ 31 | Dev: uint64(stat.Dev), 32 | Ino: uint64(stat.Ino), 33 | Mode: uint32(stat.Mode), 34 | Uid: stat.Uid, 35 | Gid: stat.Gid, 36 | Rdev: uint64(stat.Rdev), 37 | Size: int64(stat.Size), 38 | Blksize: int64(stat.Blksize), 39 | Blkcnt: int64(stat.Blocks), 40 | Atime: uint64(stat.Atim.Sec), 41 | AtimeNsec: uint64(stat.Atim.Nsec), 42 | Mtime: uint64(stat.Mtim.Sec), 43 | MtimeNsec: uint64(stat.Mtim.Nsec), 44 | Ctime: uint64(stat.Ctim.Sec), 45 | CtimeNsec: uint64(stat.Ctim.Nsec), 46 | } 47 | } else { 48 | if large { 49 | return &Linux32Stat64_x86{ 50 | Dev: uint64(stat.Dev), 51 | Ino: uint32(stat.Ino), 52 | Mode: uint32(stat.Mode), 53 | Uid: stat.Uid, 54 | Gid: stat.Gid, 55 | Rdev: uint64(stat.Rdev), 56 | Size: int64(stat.Size), 57 | Blksize: uint32(stat.Blksize), 58 | Blkcnt: uint64(stat.Blocks), 59 | Atime: uint32(stat.Atim.Sec), 60 | AtimeNsec: uint32(stat.Atim.Nsec), 61 | Mtime: uint32(stat.Mtim.Sec), 62 | MtimeNsec: uint32(stat.Mtim.Nsec), 63 | Ctime: uint32(stat.Ctim.Sec), 64 | CtimeNsec: uint32(stat.Ctim.Nsec), 65 | LongIno: uint64(stat.Ino), 66 | } 67 | } 68 | return &Linux32Stat_x86{ 69 | Dev: uint32(stat.Dev), 70 | Ino: uint32(stat.Ino), 71 | Mode: uint16(stat.Mode), 72 | Uid: stat.Uid, 73 | Gid: stat.Gid, 74 | Rdev: uint32(stat.Rdev), 75 | Size: uint32(stat.Size), 76 | Blksize: uint32(stat.Blksize), 77 | Blkcnt: uint32(stat.Blocks), 78 | Atime: uint32(stat.Atim.Sec), 79 | AtimeNsec: uint32(stat.Atim.Nsec), 80 | Mtime: uint32(stat.Mtim.Sec), 81 | MtimeNsec: uint32(stat.Mtim.Nsec), 82 | Ctime: uint32(stat.Ctim.Sec), 83 | CtimeNsec: uint32(stat.Ctim.Nsec), 84 | } 85 | } 86 | } 87 | 88 | func NewDarwinStat(stat *syscall.Stat_t, bits uint, large bool) interface{} { 89 | if bits == 64 { 90 | return &DarwinStat64{ 91 | Dev: int32(stat.Dev), 92 | Mode: uint16(stat.Mode), 93 | Ino: uint64(stat.Ino), 94 | Uid: uint32(stat.Uid), 95 | Gid: uint32(stat.Gid), 96 | Rdev: int32(stat.Rdev), 97 | Size: int64(stat.Size), 98 | Blksize: int32(stat.Blksize), 99 | Blkcnt: int64(stat.Blocks), 100 | Atime: int64(stat.Atim.Sec), 101 | AtimeNsec: int64(stat.Atim.Nsec), 102 | Mtime: int64(stat.Mtim.Sec), 103 | MtimeNsec: int64(stat.Mtim.Nsec), 104 | Ctime: int64(stat.Ctim.Sec), 105 | CtimeNsec: int64(stat.Ctim.Nsec), 106 | } 107 | } else { 108 | panic("darwin stat struct unimplemented") 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /go/models/config.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "io" 5 | "os" 6 | "path/filepath" 7 | "strings" 8 | ) 9 | 10 | type TraceConfig struct { 11 | Tracefile string 12 | TraceWriter io.WriteCloser 13 | 14 | Everything bool // enables all other flags 15 | // TODO: what about only tracing specific address ranges? 16 | Block bool // implied by Ins 17 | Ins bool 18 | Mem bool 19 | Reg bool 20 | SpecialReg bool 21 | Sys bool 22 | 23 | OpCallback []func(Op) 24 | 25 | // Filters 26 | LoopCollapse int 27 | // TODO: Maybe CollapseSideeffect 28 | // (Collapse a basic block's side effects into a single one) 29 | MemBatch bool 30 | Match []string 31 | MatchDepth int 32 | } 33 | 34 | func (t *TraceConfig) Any() bool { 35 | return t.Everything || t.Block || t.Ins || t.Mem || t.SpecialReg || t.Sys 36 | } 37 | 38 | func (t *TraceConfig) Init() { 39 | if t.Ins { 40 | t.Block = true 41 | } 42 | // mtrace is required as the UI doesn't have access to main memory anymore 43 | if t.Ins || t.Block { 44 | t.Mem = true 45 | } 46 | if t.Everything { 47 | t.Block = true 48 | t.Ins = true 49 | t.Mem = true 50 | t.Reg = true 51 | t.SpecialReg = true 52 | t.Sys = true 53 | } 54 | } 55 | 56 | type Config struct { 57 | Output io.WriteCloser 58 | 59 | Env []string 60 | Args []string 61 | 62 | Color bool 63 | ForceBase uint64 64 | ForceInterpBase uint64 65 | LoadPrefix string 66 | NativeFallback bool 67 | SkipInterp bool 68 | Strsize int 69 | Verbose bool 70 | 71 | Trace TraceConfig 72 | Rewind bool 73 | UI bool 74 | 75 | SymFile bool 76 | 77 | BlockSyscalls bool 78 | StubSyscalls bool 79 | InsCount bool 80 | 81 | PrefixArgs []string 82 | 83 | // TODO: UI sub config 84 | Demangle bool 85 | DisBytes bool 86 | 87 | // TODO: Maybe add option to load whole binary into trace 88 | // (For replay and such). 89 | SourcePaths []string 90 | TraceSource bool 91 | } 92 | 93 | func (c *Config) Init() *Config { 94 | if c == nil { 95 | return (&Config{}).Init() 96 | } 97 | if c.Output == nil { 98 | c.Output = os.Stderr 99 | } 100 | c.Trace.Init() 101 | return c 102 | } 103 | 104 | func (c *Config) resolveSymlink(path, target string, force bool) string { 105 | link, err := os.Lstat(path) 106 | if err == nil && link.Mode()&os.ModeSymlink != 0 { 107 | if linked, err := os.Readlink(path); err == nil { 108 | if !filepath.IsAbs(linked) { 109 | linked = filepath.Join(filepath.Dir(path), linked) 110 | return c.PrefixPath(linked, false) 111 | } 112 | return c.PrefixPath(linked, force) 113 | } 114 | } 115 | exists := !os.IsNotExist(err) 116 | if force || exists { 117 | return path 118 | } 119 | return target 120 | } 121 | 122 | func (c *Config) PrefixPath(path string, force bool) string { 123 | if c.LoadPrefix == "" { 124 | return path 125 | } 126 | target := path 127 | if filepath.IsAbs(path) && !strings.HasPrefix(path, c.LoadPrefix) { 128 | target = filepath.Join(c.LoadPrefix, path) 129 | } 130 | resolved := c.resolveSymlink(target, path, force) 131 | if _, err := os.Stat(resolved); force || err == nil { 132 | return resolved 133 | } 134 | return path 135 | } 136 | 137 | func (c *Config) PrefixRel(path string) string { 138 | // returns an absolute path inside the load prefix 139 | // as a path relative to the prefix base 140 | if !filepath.IsAbs(path) { 141 | return path 142 | } 143 | rel, err := filepath.Rel(c.LoadPrefix, path) 144 | if err != nil { 145 | return path 146 | } 147 | split := filepath.SplitList(rel) 148 | if len(split) > 0 && split[0] == ".." { 149 | return path 150 | } 151 | return rel 152 | } 153 | -------------------------------------------------------------------------------- /go/arch/x86_64/linux.go: -------------------------------------------------------------------------------- 1 | package x86_64 2 | 3 | import ( 4 | "fmt" 5 | "github.com/lunixbochs/ghostrace/ghost/sys/num" 6 | "github.com/pkg/errors" 7 | uc "github.com/unicorn-engine/unicorn/bindings/go/unicorn" 8 | 9 | "github.com/lunixbochs/usercorn/go/arch/x86" 10 | "github.com/lunixbochs/usercorn/go/kernel/common" 11 | "github.com/lunixbochs/usercorn/go/kernel/linux" 12 | "github.com/lunixbochs/usercorn/go/models" 13 | "github.com/lunixbochs/usercorn/go/models/cpu" 14 | ) 15 | 16 | type LinuxKernel struct { 17 | *linux.LinuxKernel 18 | } 19 | 20 | func setupVsyscall(u models.Usercorn, kernel *LinuxKernel) error { 21 | base := uint64(0xffffffffff600000) 22 | vgettimeofday := base + 0x0 23 | vtime := base + 0x400 24 | vgetcpu := base + 0x800 25 | 26 | // handle x86_64 vsyscall traps 27 | if err := u.MemMap(base, 0x1000, cpu.PROT_READ|cpu.PROT_EXEC); err != nil { 28 | return err 29 | } 30 | // write 'ret' to trap addrs so they return 31 | ret := []byte{0xc3} 32 | if err := u.MemWrite(vgettimeofday, ret); err != nil { 33 | return err 34 | } 35 | if err := u.MemWrite(vtime, ret); err != nil { 36 | return err 37 | } 38 | if err := u.MemWrite(vgetcpu, ret); err != nil { 39 | return err 40 | } 41 | 42 | _, err := u.HookAdd(cpu.HOOK_CODE, func(_ cpu.Cpu, addr uint64, size uint32) { 43 | switch addr { 44 | case vgettimeofday: 45 | ret, _ := u.Syscall(96, "gettimeofday", common.RegArgs(u, AbiRegs)) 46 | u.RegWrite(uc.X86_REG_RAX, ret) 47 | case vtime: 48 | ret, _ := u.Syscall(201, "time", common.RegArgs(u, AbiRegs)) 49 | u.RegWrite(uc.X86_REG_RAX, ret) 50 | case vgetcpu: 51 | ret, _ := u.Syscall(309, "getcpu", common.RegArgs(u, AbiRegs)) 52 | u.RegWrite(uc.X86_REG_RAX, ret) 53 | default: 54 | panic(fmt.Sprintf("unsupported vsyscall trap: 0x%x\n", addr)) 55 | } 56 | }, 0xffffffffff600000, 0xffffffffff601000) 57 | return errors.Wrap(err, "u.HookAdd() failed") 58 | } 59 | 60 | // TODO: put these somewhere. ghostrace maybe. 61 | const ( 62 | ARCH_SET_GS = 0x1001 63 | ARCH_SET_FS = 0x1002 64 | ARCH_GET_FS = 0x1003 65 | ARCH_GET_GS = 0x1004 66 | ) 67 | 68 | func (k *LinuxKernel) ArchPrctl(code int, addr uint64) { 69 | fsmsr := uint64(0xC0000100) 70 | gsmsr := uint64(0xC0000101) 71 | 72 | var tmp [8]byte 73 | // TODO: make SET check for valid mapped memory 74 | switch code { 75 | case ARCH_SET_FS: 76 | x86.Wrmsr(k.U, fsmsr, addr) 77 | case ARCH_SET_GS: 78 | x86.Wrmsr(k.U, gsmsr, addr) 79 | case ARCH_GET_FS: 80 | val := x86.Rdmsr(k.U, fsmsr) 81 | buf, _ := k.U.PackAddr(tmp[:], val) 82 | k.U.MemWrite(addr, buf) 83 | case ARCH_GET_GS: 84 | val := x86.Rdmsr(k.U, gsmsr) 85 | buf, _ := k.U.PackAddr(tmp[:], val) 86 | k.U.MemWrite(addr, buf) 87 | } 88 | } 89 | 90 | func LinuxKernels(u models.Usercorn) []interface{} { 91 | kernel := &LinuxKernel{linux.NewKernel()} 92 | // TODO: LinuxInit needs to have a copy of the kernel 93 | if err := setupVsyscall(u, kernel); err != nil { 94 | panic(err) 95 | } 96 | return []interface{}{kernel} 97 | } 98 | 99 | func LinuxInit(u models.Usercorn, args, env []string) error { 100 | if err := linux.StackInit(u, args, env); err != nil { 101 | return err 102 | } 103 | return AbiInit(u, LinuxSyscall) 104 | } 105 | 106 | func LinuxSyscall(u models.Usercorn) { 107 | rax, _ := u.RegRead(uc.X86_REG_RAX) 108 | name, _ := num.Linux_x86_64[int(rax)] 109 | ret, _ := u.Syscall(int(rax), name, common.RegArgs(u, AbiRegs)) 110 | u.RegWrite(uc.X86_REG_RAX, ret) 111 | } 112 | 113 | func LinuxInterrupt(u models.Usercorn, intno uint32) { 114 | if intno == 0 { 115 | u.Exit(errors.New("division by zero")) 116 | } 117 | if intno == 0x80 { 118 | LinuxSyscall(u) 119 | } 120 | } 121 | 122 | func init() { 123 | Arch.RegisterOS(&models.OS{Name: "linux", Kernels: LinuxKernels, Init: LinuxInit, Interrupt: LinuxInterrupt}) 124 | } 125 | --------------------------------------------------------------------------------