Initialize module and dependencies
This commit is contained in:
349
vendor/golang.org/x/vuln/internal/goversion/asm.go
generated
vendored
Normal file
349
vendor/golang.org/x/vuln/internal/goversion/asm.go
generated
vendored
Normal file
@@ -0,0 +1,349 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package goversion
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
type matcher [][]uint32
|
||||
|
||||
const (
|
||||
pWild uint32 = 0xff00
|
||||
pAddr uint32 = 0x10000
|
||||
pEnd uint32 = 0x20000
|
||||
pRelAddr uint32 = 0x30000
|
||||
|
||||
opMaybe = 1 + iota
|
||||
opMust
|
||||
opDone
|
||||
opAnchor = 0x100
|
||||
opSub8 = 0x200
|
||||
opFlags = opAnchor | opSub8
|
||||
)
|
||||
|
||||
var amd64Matcher = matcher{
|
||||
{opMaybe | opAnchor,
|
||||
// __rt0_amd64_darwin:
|
||||
// JMP __rt0_amd64
|
||||
0xe9, pWild | pAddr, pWild, pWild, pWild | pEnd, 0xcc, 0xcc, 0xcc,
|
||||
},
|
||||
{opMaybe,
|
||||
// _rt0_amd64_linux:
|
||||
// lea 0x8(%rsp), %rsi
|
||||
// mov (%rsp), %rdi
|
||||
// lea ADDR(%rip), %rax # main
|
||||
// jmpq *%rax
|
||||
0x48, 0x8d, 0x74, 0x24, 0x08,
|
||||
0x48, 0x8b, 0x3c, 0x24, 0x48,
|
||||
0x8d, 0x05, pWild | pAddr, pWild, pWild, pWild | pEnd,
|
||||
0xff, 0xe0,
|
||||
},
|
||||
{opMaybe,
|
||||
// _rt0_amd64_linux:
|
||||
// lea 0x8(%rsp), %rsi
|
||||
// mov (%rsp), %rdi
|
||||
// mov $ADDR, %eax # main
|
||||
// jmpq *%rax
|
||||
0x48, 0x8d, 0x74, 0x24, 0x08,
|
||||
0x48, 0x8b, 0x3c, 0x24,
|
||||
0xb8, pWild | pAddr, pWild, pWild, pWild,
|
||||
0xff, 0xe0,
|
||||
},
|
||||
{opMaybe,
|
||||
// __rt0_amd64:
|
||||
// mov (%rsp), %rdi
|
||||
// lea 8(%rsp), %rsi
|
||||
// jmp runtime.rt0_g0
|
||||
0x48, 0x8b, 0x3c, 0x24,
|
||||
0x48, 0x8d, 0x74, 0x24, 0x08,
|
||||
0xe9, pWild | pAddr, pWild, pWild, pWild | pEnd,
|
||||
0xcc, 0xcc,
|
||||
},
|
||||
{opMaybe,
|
||||
// _start (toward end)
|
||||
// lea __libc_csu_fini(%rip), %r8
|
||||
// lea __libc_csu_init(%rip), %rcx
|
||||
// lea ADDR(%rip), %rdi # main
|
||||
// callq *xxx(%rip)
|
||||
0x4c, 0x8d, 0x05, pWild, pWild, pWild, pWild,
|
||||
0x48, 0x8d, 0x0d, pWild, pWild, pWild, pWild,
|
||||
0x48, 0x8d, 0x3d, pWild | pAddr, pWild, pWild, pWild | pEnd,
|
||||
0xff, 0x15,
|
||||
},
|
||||
{opMaybe,
|
||||
// _start (toward end)
|
||||
// push %rsp (1)
|
||||
// mov $__libc_csu_fini, %r8 (7)
|
||||
// mov $__libc_csu_init, %rcx (7)
|
||||
// mov $ADDR, %rdi # main (7)
|
||||
// callq *xxx(%rip)
|
||||
0x54,
|
||||
0x49, 0xc7, 0xc0, pWild, pWild, pWild, pWild,
|
||||
0x48, 0xc7, 0xc1, pWild, pWild, pWild, pWild,
|
||||
0x48, 0xc7, 0xc7, pAddr | pWild, pWild, pWild, pWild,
|
||||
},
|
||||
{opMaybe | opAnchor,
|
||||
// main:
|
||||
// lea ADDR(%rip), %rax # rt0_go
|
||||
// jmpq *%rax
|
||||
0x48, 0x8d, 0x05, pWild | pAddr, pWild, pWild, pWild | pEnd,
|
||||
0xff, 0xe0,
|
||||
},
|
||||
{opMaybe | opAnchor,
|
||||
// main:
|
||||
// mov $ADDR, %eax
|
||||
// jmpq *%rax
|
||||
0xb8, pWild | pAddr, pWild, pWild, pWild,
|
||||
0xff, 0xe0,
|
||||
},
|
||||
{opMaybe | opAnchor,
|
||||
// main:
|
||||
// JMP runtime.rt0_go(SB)
|
||||
0xe9, pWild | pAddr, pWild, pWild, pWild | pEnd, 0xcc, 0xcc, 0xcc,
|
||||
},
|
||||
{opMust | opAnchor,
|
||||
// rt0_go:
|
||||
// mov %rdi, %rax
|
||||
// mov %rsi, %rbx
|
||||
// sub %0x27, %rsp
|
||||
// and $0xfffffffffffffff0,%rsp
|
||||
// mov %rax,0x10(%rsp)
|
||||
// mov %rbx,0x18(%rsp)
|
||||
0x48, 0x89, 0xf8,
|
||||
0x48, 0x89, 0xf3,
|
||||
0x48, 0x83, 0xec, 0x27,
|
||||
0x48, 0x83, 0xe4, 0xf0,
|
||||
0x48, 0x89, 0x44, 0x24, 0x10,
|
||||
0x48, 0x89, 0x5c, 0x24, 0x18,
|
||||
},
|
||||
{opMust,
|
||||
// later in rt0_go:
|
||||
// mov %eax, (%rsp)
|
||||
// mov 0x18(%rsp), %rax
|
||||
// mov %rax, 0x8(%rsp)
|
||||
// callq runtime.args
|
||||
// callq runtime.osinit
|
||||
// callq runtime.schedinit (ADDR)
|
||||
0x89, 0x04, 0x24,
|
||||
0x48, 0x8b, 0x44, 0x24, 0x18,
|
||||
0x48, 0x89, 0x44, 0x24, 0x08,
|
||||
0xe8, pWild, pWild, pWild, pWild,
|
||||
0xe8, pWild, pWild, pWild, pWild,
|
||||
0xe8, pWild, pWild, pWild, pWild,
|
||||
},
|
||||
{opMaybe,
|
||||
// later in rt0_go:
|
||||
// mov %eax, (%rsp)
|
||||
// mov 0x18(%rsp), %rax
|
||||
// mov %rax, 0x8(%rsp)
|
||||
// callq runtime.args
|
||||
// callq runtime.osinit
|
||||
// callq runtime.schedinit (ADDR)
|
||||
// lea other(%rip), %rdi
|
||||
0x89, 0x04, 0x24,
|
||||
0x48, 0x8b, 0x44, 0x24, 0x18,
|
||||
0x48, 0x89, 0x44, 0x24, 0x08,
|
||||
0xe8, pWild, pWild, pWild, pWild,
|
||||
0xe8, pWild, pWild, pWild, pWild,
|
||||
0xe8, pWild | pAddr, pWild, pWild, pWild | pEnd,
|
||||
0x48, 0x8d, 0x05,
|
||||
},
|
||||
{opMaybe,
|
||||
// later in rt0_go:
|
||||
// mov %eax, (%rsp)
|
||||
// mov 0x18(%rsp), %rax
|
||||
// mov %rax, 0x8(%rsp)
|
||||
// callq runtime.args
|
||||
// callq runtime.osinit
|
||||
// callq runtime.hashinit
|
||||
// callq runtime.schedinit (ADDR)
|
||||
// pushq $main.main
|
||||
0x89, 0x04, 0x24,
|
||||
0x48, 0x8b, 0x44, 0x24, 0x18,
|
||||
0x48, 0x89, 0x44, 0x24, 0x08,
|
||||
0xe8, pWild, pWild, pWild, pWild,
|
||||
0xe8, pWild, pWild, pWild, pWild,
|
||||
0xe8, pWild, pWild, pWild, pWild,
|
||||
0xe8, pWild | pAddr, pWild, pWild, pWild | pEnd,
|
||||
0x68,
|
||||
},
|
||||
{opDone | opSub8,
|
||||
// schedinit (toward end)
|
||||
// mov ADDR(%rip), %rax
|
||||
// test %rax, %rax
|
||||
// jne <short>
|
||||
// movq $0x7, ADDR(%rip)
|
||||
//
|
||||
0x48, 0x8b, 0x05, pWild, pWild, pWild, pWild,
|
||||
0x48, 0x85, 0xc0,
|
||||
0x75, pWild,
|
||||
0x48, 0xc7, 0x05, pWild | pAddr, pWild, pWild, pWild, 0x07, 0x00, 0x00, 0x00 | pEnd,
|
||||
},
|
||||
{opDone | opSub8,
|
||||
// schedinit (toward end)
|
||||
// mov ADDR(%rip), %rbx
|
||||
// cmp $0x0, %rbx
|
||||
// jne <short>
|
||||
// lea "unknown"(%rip), %rbx
|
||||
// mov %rbx, ADDR(%rip)
|
||||
// movq $7, (ADDR+8)(%rip)
|
||||
0x48, 0x8b, 0x1d, pWild, pWild, pWild, pWild,
|
||||
0x48, 0x83, 0xfb, 0x00,
|
||||
0x75, pWild,
|
||||
0x48, 0x8d, 0x1d, pWild, pWild, pWild, pWild,
|
||||
0x48, 0x89, 0x1d, pWild, pWild, pWild, pWild,
|
||||
0x48, 0xc7, 0x05, pWild | pAddr, pWild, pWild, pWild, 0x07, 0x00, 0x00, 0x00 | pEnd,
|
||||
},
|
||||
{opDone,
|
||||
// schedinit (toward end)
|
||||
// cmpq $0x0, ADDR(%rip)
|
||||
// jne <short>
|
||||
// lea "unknown"(%rip), %rax
|
||||
// mov %rax, ADDR(%rip)
|
||||
// lea ADDR(%rip), %rax
|
||||
// movq $7, 8(%rax)
|
||||
0x48, 0x83, 0x3d, pWild | pAddr, pWild, pWild, pWild, 0x00,
|
||||
0x75, pWild,
|
||||
0x48, 0x8d, 0x05, pWild, pWild, pWild, pWild,
|
||||
0x48, 0x89, 0x05, pWild, pWild, pWild, pWild,
|
||||
0x48, 0x8d, 0x05, pWild | pAddr, pWild, pWild, pWild | pEnd,
|
||||
0x48, 0xc7, 0x40, 0x08, 0x07, 0x00, 0x00, 0x00,
|
||||
},
|
||||
{opDone,
|
||||
// schedinit (toward end)
|
||||
// cmpq $0x0, ADDR(%rip)
|
||||
// jne <short>
|
||||
// movq $0x7, ADDR(%rip)
|
||||
0x48, 0x83, 0x3d, pWild | pAddr, pWild, pWild, pWild, 0x00,
|
||||
0x75, pWild,
|
||||
0x48, 0xc7, 0x05 | pEnd, pWild | pAddr, pWild, pWild, pWild, 0x07, 0x00, 0x00, 0x00,
|
||||
},
|
||||
{opDone,
|
||||
// test %eax, %eax
|
||||
// jne <later>
|
||||
// lea "unknown"(RIP), %rax
|
||||
// mov %rax, ADDR(%rip)
|
||||
0x48, 0x85, 0xc0, 0x75, pWild, 0x48, 0x8d, 0x05, pWild, pWild, pWild, pWild, 0x48, 0x89, 0x05, pWild | pAddr, pWild, pWild, pWild | pEnd,
|
||||
},
|
||||
{opDone,
|
||||
// schedinit (toward end)
|
||||
// mov ADDR(%rip), %rcx
|
||||
// test %rcx, %rcx
|
||||
// jne <short>
|
||||
// movq $0x7, ADDR(%rip)
|
||||
//
|
||||
0x48, 0x8b, 0x0d, pWild, pWild, pWild, pWild,
|
||||
0x48, 0x85, 0xc9,
|
||||
0x75, pWild,
|
||||
0x48, 0xc7, 0x05 | pEnd, pWild | pAddr, pWild, pWild, pWild, 0x07, 0x00, 0x00, 0x00,
|
||||
},
|
||||
}
|
||||
|
||||
var DebugMatch bool
|
||||
|
||||
func (m matcher) match(f exe, addr uint64) (uint64, bool) {
|
||||
data, err := f.ReadData(addr, 512)
|
||||
if DebugMatch {
|
||||
fmt.Fprintf(os.Stderr, "data @%#x: %x\n", addr, data[:16])
|
||||
}
|
||||
if err != nil {
|
||||
if DebugMatch {
|
||||
fmt.Fprintf(os.Stderr, "match: %v\n", err)
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
if DebugMatch {
|
||||
fmt.Fprintf(os.Stderr, "data: %x\n", data[:32])
|
||||
}
|
||||
Matchers:
|
||||
for pc, p := range m {
|
||||
op := p[0]
|
||||
p = p[1:]
|
||||
Search:
|
||||
for i := 0; i <= len(data)-len(p); i++ {
|
||||
a := -1
|
||||
e := -1
|
||||
if i > 0 && op&opAnchor != 0 {
|
||||
break
|
||||
}
|
||||
for j := 0; j < len(p); j++ {
|
||||
b := byte(p[j])
|
||||
m := byte(p[j] >> 8)
|
||||
if data[i+j]&^m != b {
|
||||
continue Search
|
||||
}
|
||||
if p[j]&pAddr != 0 {
|
||||
a = j
|
||||
}
|
||||
if p[j]&pEnd != 0 {
|
||||
e = j + 1
|
||||
}
|
||||
}
|
||||
// matched
|
||||
if DebugMatch {
|
||||
fmt.Fprintf(os.Stderr, "match (%d) %#x+%d %x %x\n", pc, addr, i, p, data[i:i+len(p)])
|
||||
}
|
||||
if a != -1 {
|
||||
val := uint64(int32(binary.LittleEndian.Uint32(data[i+a:])))
|
||||
if e == -1 {
|
||||
addr = val
|
||||
} else {
|
||||
addr += uint64(i+e) + val
|
||||
}
|
||||
if op&opSub8 != 0 {
|
||||
addr -= 8
|
||||
}
|
||||
}
|
||||
if op&^opFlags == opDone {
|
||||
if DebugMatch {
|
||||
fmt.Fprintf(os.Stderr, "done %x\n", addr)
|
||||
}
|
||||
return addr, true
|
||||
}
|
||||
if a != -1 {
|
||||
// changed addr, so reload
|
||||
data, err = f.ReadData(addr, 512)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
if DebugMatch {
|
||||
fmt.Fprintf(os.Stderr, "reload @%#x: %x\n", addr, data[:32])
|
||||
}
|
||||
}
|
||||
continue Matchers
|
||||
}
|
||||
// not matched
|
||||
if DebugMatch {
|
||||
fmt.Fprintf(os.Stderr, "no match (%d) %#x %x %x\n", pc, addr, p, data[:32])
|
||||
}
|
||||
if op&^opFlags == opMust {
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
// ran off end of matcher
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func readBuildVersionX86Asm(f exe) (isGo bool, buildVersion string) {
|
||||
entry := f.Entry()
|
||||
if entry == 0 {
|
||||
if DebugMatch {
|
||||
fmt.Fprintf(os.Stderr, "missing entry!\n")
|
||||
}
|
||||
return
|
||||
}
|
||||
addr, ok := amd64Matcher.match(f, entry)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
v, err := readBuildVersion(f, addr, 16)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return true, v
|
||||
}
|
||||
324
vendor/golang.org/x/vuln/internal/goversion/exe.go
generated
vendored
Normal file
324
vendor/golang.org/x/vuln/internal/goversion/exe.go
generated
vendored
Normal file
@@ -0,0 +1,324 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package goversion
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"debug/elf"
|
||||
"debug/macho"
|
||||
"debug/pe"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
type sym struct {
|
||||
Name string
|
||||
Addr uint64
|
||||
Size uint64
|
||||
}
|
||||
|
||||
type exe interface {
|
||||
AddrSize() int // bytes
|
||||
ReadData(addr, size uint64) ([]byte, error)
|
||||
Symbols() ([]sym, error)
|
||||
SectionNames() []string
|
||||
Close() error
|
||||
ByteOrder() binary.ByteOrder
|
||||
Entry() uint64
|
||||
TextRange() (uint64, uint64)
|
||||
RODataRange() (uint64, uint64)
|
||||
}
|
||||
|
||||
func openExe(file string) (exe, error) {
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := make([]byte, 16)
|
||||
if _, err := io.ReadFull(f, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.Seek(0, 0)
|
||||
if bytes.HasPrefix(data, []byte("\x7FELF")) {
|
||||
e, err := elf.NewFile(f)
|
||||
if err != nil {
|
||||
f.Close()
|
||||
return nil, err
|
||||
}
|
||||
return &elfExe{f, e}, nil
|
||||
}
|
||||
if bytes.HasPrefix(data, []byte("MZ")) {
|
||||
e, err := pe.NewFile(f)
|
||||
if err != nil {
|
||||
f.Close()
|
||||
return nil, err
|
||||
}
|
||||
return &peExe{f, e}, nil
|
||||
}
|
||||
if bytes.HasPrefix(data, []byte("\xFE\xED\xFA")) || bytes.HasPrefix(data[1:], []byte("\xFA\xED\xFE")) {
|
||||
e, err := macho.NewFile(f)
|
||||
if err != nil {
|
||||
f.Close()
|
||||
return nil, err
|
||||
}
|
||||
return &machoExe{f, e}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("unrecognized executable format")
|
||||
}
|
||||
|
||||
type elfExe struct {
|
||||
os *os.File
|
||||
f *elf.File
|
||||
}
|
||||
|
||||
func (x *elfExe) AddrSize() int { return 0 }
|
||||
|
||||
func (x *elfExe) ByteOrder() binary.ByteOrder { return x.f.ByteOrder }
|
||||
|
||||
func (x *elfExe) Close() error {
|
||||
return x.os.Close()
|
||||
}
|
||||
|
||||
func (x *elfExe) Entry() uint64 { return x.f.Entry }
|
||||
|
||||
func (x *elfExe) ReadData(addr, size uint64) ([]byte, error) {
|
||||
for _, prog := range x.f.Progs {
|
||||
// The following line was commented from the original code.
|
||||
//fmt.Printf("%#x %#x %#x\n", addr, prog.Vaddr, prog.Vaddr+prog.Filesz)
|
||||
if prog.Vaddr <= addr && addr <= prog.Vaddr+prog.Filesz-1 {
|
||||
n := prog.Vaddr + prog.Filesz - addr
|
||||
if n > size {
|
||||
n = size
|
||||
}
|
||||
data := make([]byte, n)
|
||||
_, err := prog.ReadAt(data, int64(addr-prog.Vaddr))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("address not mapped")
|
||||
}
|
||||
|
||||
func (x *elfExe) Symbols() ([]sym, error) {
|
||||
syms, err := x.f.Symbols()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var out []sym
|
||||
for _, s := range syms {
|
||||
out = append(out, sym{s.Name, s.Value, s.Size})
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (x *elfExe) SectionNames() []string {
|
||||
var names []string
|
||||
for _, sect := range x.f.Sections {
|
||||
names = append(names, sect.Name)
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
func (x *elfExe) TextRange() (uint64, uint64) {
|
||||
for _, p := range x.f.Progs {
|
||||
if p.Type == elf.PT_LOAD && p.Flags&elf.PF_X != 0 {
|
||||
return p.Vaddr, p.Vaddr + p.Filesz
|
||||
}
|
||||
}
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
func (x *elfExe) RODataRange() (uint64, uint64) {
|
||||
for _, p := range x.f.Progs {
|
||||
if p.Type == elf.PT_LOAD && p.Flags&(elf.PF_R|elf.PF_W|elf.PF_X) == elf.PF_R {
|
||||
return p.Vaddr, p.Vaddr + p.Filesz
|
||||
}
|
||||
}
|
||||
for _, p := range x.f.Progs {
|
||||
if p.Type == elf.PT_LOAD && p.Flags&(elf.PF_R|elf.PF_W|elf.PF_X) == (elf.PF_R|elf.PF_X) {
|
||||
return p.Vaddr, p.Vaddr + p.Filesz
|
||||
}
|
||||
}
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
type peExe struct {
|
||||
os *os.File
|
||||
f *pe.File
|
||||
}
|
||||
|
||||
func (x *peExe) imageBase() uint64 {
|
||||
switch oh := x.f.OptionalHeader.(type) {
|
||||
case *pe.OptionalHeader32:
|
||||
return uint64(oh.ImageBase)
|
||||
case *pe.OptionalHeader64:
|
||||
return oh.ImageBase
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *peExe) AddrSize() int {
|
||||
if x.f.Machine == pe.IMAGE_FILE_MACHINE_AMD64 {
|
||||
return 8
|
||||
}
|
||||
return 4
|
||||
}
|
||||
|
||||
func (x *peExe) ByteOrder() binary.ByteOrder { return binary.LittleEndian }
|
||||
|
||||
func (x *peExe) Close() error {
|
||||
return x.os.Close()
|
||||
}
|
||||
|
||||
func (x *peExe) Entry() uint64 {
|
||||
switch oh := x.f.OptionalHeader.(type) {
|
||||
case *pe.OptionalHeader32:
|
||||
return uint64(oh.ImageBase + oh.AddressOfEntryPoint)
|
||||
case *pe.OptionalHeader64:
|
||||
return oh.ImageBase + uint64(oh.AddressOfEntryPoint)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *peExe) ReadData(addr, size uint64) ([]byte, error) {
|
||||
addr -= x.imageBase()
|
||||
data := make([]byte, size)
|
||||
for _, sect := range x.f.Sections {
|
||||
if uint64(sect.VirtualAddress) <= addr && addr+size-1 <= uint64(sect.VirtualAddress+sect.Size-1) {
|
||||
_, err := sect.ReadAt(data, int64(addr-uint64(sect.VirtualAddress)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("address not mapped")
|
||||
}
|
||||
|
||||
func (x *peExe) Symbols() ([]sym, error) {
|
||||
base := x.imageBase()
|
||||
var out []sym
|
||||
for _, s := range x.f.Symbols {
|
||||
if s.SectionNumber <= 0 || int(s.SectionNumber) > len(x.f.Sections) {
|
||||
continue
|
||||
}
|
||||
sect := x.f.Sections[s.SectionNumber-1]
|
||||
out = append(out, sym{s.Name, uint64(s.Value) + base + uint64(sect.VirtualAddress), 0})
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (x *peExe) SectionNames() []string {
|
||||
var names []string
|
||||
for _, sect := range x.f.Sections {
|
||||
names = append(names, sect.Name)
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
func (x *peExe) TextRange() (uint64, uint64) {
|
||||
// Assume text is first non-empty section.
|
||||
for _, sect := range x.f.Sections {
|
||||
if sect.VirtualAddress != 0 && sect.Size != 0 {
|
||||
return uint64(sect.VirtualAddress) + x.imageBase(), uint64(sect.VirtualAddress+sect.Size) + x.imageBase()
|
||||
}
|
||||
}
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
func (x *peExe) RODataRange() (uint64, uint64) {
|
||||
return x.TextRange()
|
||||
}
|
||||
|
||||
type machoExe struct {
|
||||
os *os.File
|
||||
f *macho.File
|
||||
}
|
||||
|
||||
func (x *machoExe) AddrSize() int {
|
||||
if x.f.Cpu&0x01000000 != 0 {
|
||||
return 8
|
||||
}
|
||||
return 4
|
||||
}
|
||||
|
||||
func (x *machoExe) ByteOrder() binary.ByteOrder { return x.f.ByteOrder }
|
||||
|
||||
func (x *machoExe) Close() error {
|
||||
return x.os.Close()
|
||||
}
|
||||
|
||||
func (x *machoExe) Entry() uint64 {
|
||||
for _, load := range x.f.Loads {
|
||||
b, ok := load.(macho.LoadBytes)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
// TODO: Other thread states.
|
||||
bo := x.f.ByteOrder
|
||||
const x86_THREAD_STATE64 = 4
|
||||
cmd, siz := macho.LoadCmd(bo.Uint32(b[0:4])), bo.Uint32(b[4:8])
|
||||
if cmd == macho.LoadCmdUnixThread && siz == 184 && bo.Uint32(b[8:12]) == x86_THREAD_STATE64 {
|
||||
return bo.Uint64(b[144:])
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *machoExe) ReadData(addr, size uint64) ([]byte, error) {
|
||||
data := make([]byte, size)
|
||||
for _, load := range x.f.Loads {
|
||||
seg, ok := load.(*macho.Segment)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if seg.Addr <= addr && addr+size-1 <= seg.Addr+seg.Filesz-1 {
|
||||
if seg.Name == "__PAGEZERO" {
|
||||
continue
|
||||
}
|
||||
_, err := seg.ReadAt(data, int64(addr-seg.Addr))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("address not mapped")
|
||||
}
|
||||
|
||||
func (x *machoExe) Symbols() ([]sym, error) {
|
||||
var out []sym
|
||||
for _, s := range x.f.Symtab.Syms {
|
||||
out = append(out, sym{s.Name, s.Value, 0})
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (x *machoExe) SectionNames() []string {
|
||||
var names []string
|
||||
for _, sect := range x.f.Sections {
|
||||
names = append(names, sect.Name)
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
func (x *machoExe) TextRange() (uint64, uint64) {
|
||||
// Assume text is first non-empty segment.
|
||||
for _, load := range x.f.Loads {
|
||||
seg, ok := load.(*macho.Segment)
|
||||
if ok && seg.Name != "__PAGEZERO" && seg.Addr != 0 && seg.Filesz != 0 {
|
||||
return seg.Addr, seg.Addr + seg.Filesz
|
||||
}
|
||||
}
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
func (x *machoExe) RODataRange() (uint64, uint64) {
|
||||
return x.TextRange()
|
||||
}
|
||||
246
vendor/golang.org/x/vuln/internal/goversion/read.go
generated
vendored
Normal file
246
vendor/golang.org/x/vuln/internal/goversion/read.go
generated
vendored
Normal file
@@ -0,0 +1,246 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package goversion reports the Go version used to build program executables.
|
||||
//
|
||||
// This is a copy of rsc.io/goversion/version. We renamed the package to goversion
|
||||
// to differentiate between version package in standard library that we also use.
|
||||
package goversion
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Version is the information reported by ReadExe.
|
||||
type Version struct {
|
||||
Release string // Go version (runtime.Version in the program)
|
||||
ModuleInfo string // program's module information
|
||||
BoringCrypto bool // program uses BoringCrypto
|
||||
StandardCrypto bool // program uses standard crypto (replaced by BoringCrypto)
|
||||
FIPSOnly bool // program imports "crypto/tls/fipsonly"
|
||||
}
|
||||
|
||||
// ReadExe reports information about the Go version used to build
|
||||
// the program executable named by file.
|
||||
func ReadExe(file string) (Version, error) {
|
||||
var v Version
|
||||
f, err := openExe(file)
|
||||
if err != nil {
|
||||
return v, err
|
||||
}
|
||||
defer f.Close()
|
||||
isGo := false
|
||||
for _, name := range f.SectionNames() {
|
||||
if name == ".note.go.buildid" {
|
||||
isGo = true
|
||||
}
|
||||
}
|
||||
syms, symsErr := f.Symbols()
|
||||
isGccgo := false
|
||||
for _, sym := range syms {
|
||||
name := sym.Name
|
||||
if name == "runtime.main" || name == "main.main" {
|
||||
isGo = true
|
||||
}
|
||||
if strings.HasPrefix(name, "runtime.") && strings.HasSuffix(name, "$descriptor") {
|
||||
isGccgo = true
|
||||
}
|
||||
if name == "runtime.buildVersion" {
|
||||
isGo = true
|
||||
release, err := readBuildVersion(f, sym.Addr, sym.Size)
|
||||
if err != nil {
|
||||
return v, err
|
||||
}
|
||||
v.Release = release
|
||||
}
|
||||
// Note: Using strings.HasPrefix because Go 1.17+ adds ".abi0" to many of these symbols.
|
||||
if strings.Contains(name, "_Cfunc__goboringcrypto_") || strings.HasPrefix(name, "crypto/internal/boring/sig.BoringCrypto") {
|
||||
v.BoringCrypto = true
|
||||
}
|
||||
if strings.HasPrefix(name, "crypto/internal/boring/sig.FIPSOnly") {
|
||||
v.FIPSOnly = true
|
||||
}
|
||||
for _, re := range standardCryptoNames {
|
||||
if re.MatchString(name) {
|
||||
v.StandardCrypto = true
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(name, "crypto/internal/boring/sig.StandardCrypto") {
|
||||
v.StandardCrypto = true
|
||||
}
|
||||
}
|
||||
|
||||
if DebugMatch {
|
||||
v.Release = ""
|
||||
}
|
||||
if err := findModuleInfo(&v, f); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if v.Release == "" {
|
||||
g, release := readBuildVersionX86Asm(f)
|
||||
if g {
|
||||
isGo = true
|
||||
v.Release = release
|
||||
if err := findCryptoSigs(&v, f); err != nil {
|
||||
return v, err
|
||||
}
|
||||
}
|
||||
}
|
||||
if isGccgo && v.Release == "" {
|
||||
isGo = true
|
||||
v.Release = "gccgo (version unknown)"
|
||||
}
|
||||
if !isGo && symsErr != nil {
|
||||
return v, symsErr
|
||||
}
|
||||
|
||||
if !isGo {
|
||||
return v, errors.New("not a Go executable")
|
||||
}
|
||||
if v.Release == "" {
|
||||
v.Release = "unknown Go version"
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
var re = regexp.MustCompile
|
||||
|
||||
var standardCryptoNames = []*regexp.Regexp{
|
||||
re(`^crypto/sha1\.\(\*digest\)`),
|
||||
re(`^crypto/sha256\.\(\*digest\)`),
|
||||
re(`^crypto/rand\.\(\*devReader\)`),
|
||||
re(`^crypto/rsa\.encrypt(\.abi.)?$`),
|
||||
re(`^crypto/rsa\.decrypt(\.abi.)?$`),
|
||||
}
|
||||
|
||||
func readBuildVersion(f exe, addr, size uint64) (string, error) {
|
||||
if size == 0 {
|
||||
size = uint64(f.AddrSize() * 2)
|
||||
}
|
||||
if size != 8 && size != 16 {
|
||||
return "", fmt.Errorf("invalid size for runtime.buildVersion")
|
||||
}
|
||||
data, err := f.ReadData(addr, size)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("reading runtime.buildVersion: %v", err)
|
||||
}
|
||||
|
||||
if size == 8 {
|
||||
addr = uint64(f.ByteOrder().Uint32(data))
|
||||
size = uint64(f.ByteOrder().Uint32(data[4:]))
|
||||
} else {
|
||||
addr = f.ByteOrder().Uint64(data)
|
||||
size = f.ByteOrder().Uint64(data[8:])
|
||||
}
|
||||
if size > 1000 {
|
||||
return "", fmt.Errorf("implausible string size %d for runtime.buildVersion", size)
|
||||
}
|
||||
|
||||
data, err = f.ReadData(addr, size)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("reading runtime.buildVersion string data: %v", err)
|
||||
}
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
// Code signatures that indicate BoringCrypto or crypto/internal/fipsonly.
|
||||
// These are not byte literals in order to avoid the actual
|
||||
// byte signatures appearing in the goversion binary,
|
||||
// because on some systems you can't tell rodata from text.
|
||||
var (
|
||||
sigBoringCrypto, _ = hex.DecodeString("EB1DF448F44BF4B332F52813A3B450D441CC2485F001454E92101B1D2F1950C3")
|
||||
sigStandardCrypto, _ = hex.DecodeString("EB1DF448F44BF4BAEE4DFA9851CA56A91145E83E99C59CF911CB8E80DAF12FC3")
|
||||
sigFIPSOnly, _ = hex.DecodeString("EB1DF448F44BF4363CB9CE9D68047D31F28D325D5CA5873F5D80CAF6D6151BC3")
|
||||
)
|
||||
|
||||
func findCryptoSigs(v *Version, f exe) error {
|
||||
const maxSigLen = 1 << 10
|
||||
start, end := f.TextRange()
|
||||
for addr := start; addr < end; {
|
||||
size := uint64(1 << 20)
|
||||
if end-addr < size {
|
||||
size = end - addr
|
||||
}
|
||||
data, err := f.ReadData(addr, size)
|
||||
if err != nil {
|
||||
return fmt.Errorf("reading text: %v", err)
|
||||
}
|
||||
if haveSig(data, sigBoringCrypto) {
|
||||
v.BoringCrypto = true
|
||||
}
|
||||
if haveSig(data, sigFIPSOnly) {
|
||||
v.FIPSOnly = true
|
||||
}
|
||||
if haveSig(data, sigStandardCrypto) {
|
||||
v.StandardCrypto = true
|
||||
}
|
||||
if addr+size < end {
|
||||
size -= maxSigLen
|
||||
}
|
||||
addr += size
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func haveSig(data, sig []byte) bool {
|
||||
const align = 16
|
||||
for {
|
||||
i := bytes.Index(data, sig)
|
||||
if i < 0 {
|
||||
return false
|
||||
}
|
||||
if i&(align-1) == 0 {
|
||||
return true
|
||||
}
|
||||
// Found unaligned match; unexpected but
|
||||
// skip to next aligned boundary and keep searching.
|
||||
data = data[(i+align-1)&^(align-1):]
|
||||
}
|
||||
}
|
||||
|
||||
func findModuleInfo(v *Version, f exe) error {
|
||||
const maxModInfo = 128 << 10
|
||||
start, end := f.RODataRange()
|
||||
for addr := start; addr < end; {
|
||||
size := uint64(4 << 20)
|
||||
if end-addr < size {
|
||||
size = end - addr
|
||||
}
|
||||
data, err := f.ReadData(addr, size)
|
||||
if err != nil {
|
||||
return fmt.Errorf("reading text: %v", err)
|
||||
}
|
||||
if haveModuleInfo(data, v) {
|
||||
return nil
|
||||
}
|
||||
if addr+size < end {
|
||||
size -= maxModInfo
|
||||
}
|
||||
addr += size
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
infoStart, _ = hex.DecodeString("3077af0c9274080241e1c107e6d618e6")
|
||||
infoEnd, _ = hex.DecodeString("f932433186182072008242104116d8f2")
|
||||
)
|
||||
|
||||
func haveModuleInfo(data []byte, v *Version) bool {
|
||||
i := bytes.Index(data, infoStart)
|
||||
if i < 0 {
|
||||
return false
|
||||
}
|
||||
j := bytes.Index(data[i:], infoEnd)
|
||||
if j < 0 {
|
||||
return false
|
||||
}
|
||||
v.ModuleInfo = string(data[i+len(infoStart) : i+j])
|
||||
return true
|
||||
}
|
||||
Reference in New Issue
Block a user