Initialize module and dependencies
This commit is contained in:
9
vendor/golang.org/x/vuln/internal/buildinfo/README.md
generated
vendored
Normal file
9
vendor/golang.org/x/vuln/internal/buildinfo/README.md
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
This code is a copied and slightly modified subset of go/src/debug/buildinfo.
|
||||
|
||||
It contains logic for parsing Go binary files for the purpose of extracting
|
||||
module dependency and symbol table information.
|
||||
|
||||
Logic added by vulncheck is located in files with "additions_" prefix.
|
||||
|
||||
Within the originally named files, changed or added logic is annotated with
|
||||
a comment starting with "Addition:".
|
||||
257
vendor/golang.org/x/vuln/internal/buildinfo/additions_buildinfo.go
generated
vendored
Normal file
257
vendor/golang.org/x/vuln/internal/buildinfo/additions_buildinfo.go
generated
vendored
Normal file
@@ -0,0 +1,257 @@
|
||||
// Copyright 2022 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.
|
||||
|
||||
//go:build go1.18
|
||||
// +build go1.18
|
||||
|
||||
package buildinfo
|
||||
|
||||
// This file adds to buildinfo the functionality for extracting the PCLN table.
|
||||
|
||||
import (
|
||||
"debug/elf"
|
||||
"debug/macho"
|
||||
"debug/pe"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// ErrNoSymbols represents non-existence of symbol
|
||||
// table in binaries supported by buildinfo.
|
||||
var ErrNoSymbols = errors.New("no symbol section")
|
||||
|
||||
// SymbolInfo is derived from cmd/internal/objfile/elf.go:symbols, symbolData.
|
||||
func (x *elfExe) SymbolInfo(name string) (uint64, uint64, io.ReaderAt, error) {
|
||||
sym, err := x.lookupSymbol(name)
|
||||
if err != nil || sym == nil {
|
||||
if errors.Is(err, elf.ErrNoSymbols) {
|
||||
return 0, 0, nil, ErrNoSymbols
|
||||
}
|
||||
return 0, 0, nil, fmt.Errorf("no symbol %q", name)
|
||||
}
|
||||
prog := x.progContaining(sym.Value)
|
||||
if prog == nil {
|
||||
return 0, 0, nil, fmt.Errorf("no Prog containing value %d for %q", sym.Value, name)
|
||||
}
|
||||
return sym.Value, prog.Vaddr, prog.ReaderAt, nil
|
||||
}
|
||||
|
||||
func (x *elfExe) lookupSymbol(name string) (*elf.Symbol, error) {
|
||||
x.symbolsOnce.Do(func() {
|
||||
syms, err := x.f.Symbols()
|
||||
if err != nil {
|
||||
x.symbolsErr = err
|
||||
return
|
||||
}
|
||||
x.symbols = make(map[string]*elf.Symbol, len(syms))
|
||||
for _, s := range syms {
|
||||
s := s // make a copy to prevent aliasing
|
||||
x.symbols[s.Name] = &s
|
||||
}
|
||||
})
|
||||
if x.symbolsErr != nil {
|
||||
return nil, x.symbolsErr
|
||||
}
|
||||
return x.symbols[name], nil
|
||||
}
|
||||
|
||||
func (x *elfExe) progContaining(addr uint64) *elf.Prog {
|
||||
for _, p := range x.f.Progs {
|
||||
if addr >= p.Vaddr && addr < p.Vaddr+p.Filesz {
|
||||
return p
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const go12magic = 0xfffffffb
|
||||
const go116magic = 0xfffffffa
|
||||
|
||||
// PCLNTab is derived from cmd/internal/objfile/elf.go:pcln.
|
||||
func (x *elfExe) PCLNTab() ([]byte, uint64) {
|
||||
var offset uint64
|
||||
text := x.f.Section(".text")
|
||||
if text != nil {
|
||||
offset = text.Offset
|
||||
}
|
||||
pclntab := x.f.Section(".gopclntab")
|
||||
if pclntab == nil {
|
||||
// Addition: this code is added to support some form of stripping.
|
||||
pclntab = x.f.Section(".data.rel.ro.gopclntab")
|
||||
if pclntab == nil {
|
||||
pclntab = x.f.Section(".data.rel.ro")
|
||||
if pclntab == nil {
|
||||
return nil, 0
|
||||
}
|
||||
// Possibly the PCLN table has been stuck in the .data.rel.ro section, but without
|
||||
// its own section header. We can search for for the start by looking for the four
|
||||
// byte magic and the go magic.
|
||||
b, err := pclntab.Data()
|
||||
if err != nil {
|
||||
return nil, 0
|
||||
}
|
||||
// TODO(rolandshoemaker): I'm not sure if the 16 byte increment during the search is
|
||||
// actually correct. During testing it worked, but that may be because I got lucky
|
||||
// with the binary I was using, and we need to do four byte jumps to exhaustively
|
||||
// search the section?
|
||||
for i := 0; i < len(b); i += 16 {
|
||||
if len(b)-i > 16 && b[i+4] == 0 && b[i+5] == 0 &&
|
||||
(b[i+6] == 1 || b[i+6] == 2 || b[i+6] == 4) &&
|
||||
(b[i+7] == 4 || b[i+7] == 8) {
|
||||
// Also check for the go magic
|
||||
leMagic := binary.LittleEndian.Uint32(b[i:])
|
||||
beMagic := binary.BigEndian.Uint32(b[i:])
|
||||
switch {
|
||||
case leMagic == go12magic:
|
||||
fallthrough
|
||||
case beMagic == go12magic:
|
||||
fallthrough
|
||||
case leMagic == go116magic:
|
||||
fallthrough
|
||||
case beMagic == go116magic:
|
||||
return b[i:], offset
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
b, err := pclntab.Data()
|
||||
if err != nil {
|
||||
return nil, 0
|
||||
}
|
||||
return b, offset
|
||||
}
|
||||
|
||||
// SymbolInfo is derived from cmd/internal/objfile/pe.go:findPESymbol, loadPETable.
|
||||
func (x *peExe) SymbolInfo(name string) (uint64, uint64, io.ReaderAt, error) {
|
||||
sym, err := x.lookupSymbol(name)
|
||||
if err != nil {
|
||||
return 0, 0, nil, err
|
||||
}
|
||||
if sym == nil {
|
||||
return 0, 0, nil, fmt.Errorf("no symbol %q", name)
|
||||
}
|
||||
sect := x.f.Sections[sym.SectionNumber-1]
|
||||
// In PE, the symbol's value is the offset from the section start.
|
||||
return uint64(sym.Value), 0, sect.ReaderAt, nil
|
||||
}
|
||||
|
||||
func (x *peExe) lookupSymbol(name string) (*pe.Symbol, error) {
|
||||
x.symbolsOnce.Do(func() {
|
||||
x.symbols = make(map[string]*pe.Symbol, len(x.f.Symbols))
|
||||
if len(x.f.Symbols) == 0 {
|
||||
x.symbolsErr = ErrNoSymbols
|
||||
return
|
||||
}
|
||||
for _, s := range x.f.Symbols {
|
||||
x.symbols[s.Name] = s
|
||||
}
|
||||
})
|
||||
if x.symbolsErr != nil {
|
||||
return nil, x.symbolsErr
|
||||
}
|
||||
return x.symbols[name], nil
|
||||
}
|
||||
|
||||
// PCLNTab is derived from cmd/internal/objfile/pe.go:pcln.
|
||||
// Assumes that the underlying symbol table exists, otherwise
|
||||
// it might panic.
|
||||
func (x *peExe) PCLNTab() ([]byte, uint64) {
|
||||
var textOffset uint64
|
||||
for _, section := range x.f.Sections {
|
||||
if section.Name == ".text" {
|
||||
textOffset = uint64(section.Offset)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var start, end int64
|
||||
var section int
|
||||
if s, _ := x.lookupSymbol("runtime.pclntab"); s != nil {
|
||||
start = int64(s.Value)
|
||||
section = int(s.SectionNumber - 1)
|
||||
}
|
||||
if s, _ := x.lookupSymbol("runtime.epclntab"); s != nil {
|
||||
end = int64(s.Value)
|
||||
}
|
||||
if start == 0 || end == 0 {
|
||||
return nil, 0
|
||||
}
|
||||
offset := int64(x.f.Sections[section].Offset) + start
|
||||
size := end - start
|
||||
|
||||
pclntab := make([]byte, size)
|
||||
if _, err := x.r.ReadAt(pclntab, offset); err != nil {
|
||||
return nil, 0
|
||||
}
|
||||
return pclntab, textOffset
|
||||
}
|
||||
|
||||
// SymbolInfo is derived from cmd/internal/objfile/macho.go:symbols.
|
||||
func (x *machoExe) SymbolInfo(name string) (uint64, uint64, io.ReaderAt, error) {
|
||||
sym, err := x.lookupSymbol(name)
|
||||
if err != nil {
|
||||
return 0, 0, nil, err
|
||||
}
|
||||
if sym == nil {
|
||||
return 0, 0, nil, fmt.Errorf("no symbol %q", name)
|
||||
}
|
||||
seg := x.segmentContaining(sym.Value)
|
||||
if seg == nil {
|
||||
return 0, 0, nil, fmt.Errorf("no Segment containing value %d for %q", sym.Value, name)
|
||||
}
|
||||
return sym.Value, seg.Addr, seg.ReaderAt, nil
|
||||
}
|
||||
|
||||
func (x *machoExe) lookupSymbol(name string) (*macho.Symbol, error) {
|
||||
const mustExistSymbol = "runtime.main"
|
||||
x.symbolsOnce.Do(func() {
|
||||
x.symbols = make(map[string]*macho.Symbol, len(x.f.Symtab.Syms))
|
||||
for _, s := range x.f.Symtab.Syms {
|
||||
s := s // make a copy to prevent aliasing
|
||||
x.symbols[s.Name] = &s
|
||||
}
|
||||
// In the presence of stripping, the symbol table for darwin
|
||||
// binaries will not be empty, but the program symbols will
|
||||
// be missing.
|
||||
if _, ok := x.symbols[mustExistSymbol]; !ok {
|
||||
x.symbolsErr = ErrNoSymbols
|
||||
}
|
||||
})
|
||||
|
||||
if x.symbolsErr != nil {
|
||||
return nil, x.symbolsErr
|
||||
}
|
||||
return x.symbols[name], nil
|
||||
}
|
||||
|
||||
func (x *machoExe) segmentContaining(addr uint64) *macho.Segment {
|
||||
for _, load := range x.f.Loads {
|
||||
seg, ok := load.(*macho.Segment)
|
||||
if ok && seg.Addr <= addr && addr <= seg.Addr+seg.Filesz-1 && seg.Name != "__PAGEZERO" {
|
||||
return seg
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SymbolInfo is derived from cmd/internal/objfile/macho.go:pcln.
|
||||
func (x *machoExe) PCLNTab() ([]byte, uint64) {
|
||||
var textOffset uint64
|
||||
text := x.f.Section("__text")
|
||||
if text != nil {
|
||||
textOffset = uint64(text.Offset)
|
||||
}
|
||||
pclntab := x.f.Section("__gopclntab")
|
||||
if pclntab == nil {
|
||||
return nil, 0
|
||||
}
|
||||
b, err := pclntab.Data()
|
||||
if err != nil {
|
||||
return nil, 0
|
||||
}
|
||||
return b, textOffset
|
||||
}
|
||||
160
vendor/golang.org/x/vuln/internal/buildinfo/additions_scan.go
generated
vendored
Normal file
160
vendor/golang.org/x/vuln/internal/buildinfo/additions_scan.go
generated
vendored
Normal file
@@ -0,0 +1,160 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
//go:build go1.18
|
||||
// +build go1.18
|
||||
|
||||
package buildinfo
|
||||
|
||||
// Code in this package is dervied from src/cmd/go/internal/version/version.go
|
||||
// and cmd/go/internal/version/exe.go.
|
||||
|
||||
import (
|
||||
"debug/buildinfo"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/packages"
|
||||
"golang.org/x/vuln/internal/gosym"
|
||||
"golang.org/x/vuln/internal/goversion"
|
||||
)
|
||||
|
||||
func debugModulesToPackagesModules(debugModules []*debug.Module) []*packages.Module {
|
||||
packagesModules := make([]*packages.Module, len(debugModules))
|
||||
for i, mod := range debugModules {
|
||||
packagesModules[i] = &packages.Module{
|
||||
Path: mod.Path,
|
||||
Version: mod.Version,
|
||||
}
|
||||
if mod.Replace != nil {
|
||||
packagesModules[i].Replace = &packages.Module{
|
||||
Path: mod.Replace.Path,
|
||||
Version: mod.Replace.Version,
|
||||
}
|
||||
}
|
||||
}
|
||||
return packagesModules
|
||||
}
|
||||
|
||||
type Symbol struct {
|
||||
Pkg string `json:"pkg,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
// ExtractPackagesAndSymbols extracts symbols, packages, modules from
|
||||
// Go binary file as well as bin's metadata.
|
||||
//
|
||||
// If the symbol table is not available, such as in the case of stripped
|
||||
// binaries, returns module and binary info but without the symbol info.
|
||||
func ExtractPackagesAndSymbols(file string) ([]*packages.Module, []Symbol, *debug.BuildInfo, error) {
|
||||
bin, err := os.Open(file)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
defer bin.Close()
|
||||
|
||||
bi, err := buildinfo.Read(bin)
|
||||
if err != nil {
|
||||
// It could be that bin is an ancient Go binary.
|
||||
v, err := goversion.ReadExe(file)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
bi := &debug.BuildInfo{
|
||||
GoVersion: v.Release,
|
||||
Main: debug.Module{Path: v.ModuleInfo},
|
||||
}
|
||||
// We cannot analyze symbol tables of ancient binaries.
|
||||
return nil, nil, bi, nil
|
||||
}
|
||||
|
||||
funcSymName := gosym.FuncSymName(bi.GoVersion)
|
||||
if funcSymName == "" {
|
||||
return nil, nil, nil, fmt.Errorf("binary built using unsupported Go version: %q", bi.GoVersion)
|
||||
}
|
||||
|
||||
x, err := openExe(bin)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
value, base, r, err := x.SymbolInfo(funcSymName)
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrNoSymbols) {
|
||||
// bin is stripped, so return just module info and metadata.
|
||||
return debugModulesToPackagesModules(bi.Deps), nil, bi, nil
|
||||
}
|
||||
return nil, nil, nil, fmt.Errorf("reading %v: %v", funcSymName, err)
|
||||
}
|
||||
|
||||
pclntab, textOffset := x.PCLNTab()
|
||||
if pclntab == nil {
|
||||
// If we have build information, but not PCLN table, fall
|
||||
// back to much higher granularity vulnerability checking.
|
||||
return debugModulesToPackagesModules(bi.Deps), nil, bi, nil
|
||||
}
|
||||
lineTab := gosym.NewLineTable(pclntab, textOffset)
|
||||
if lineTab == nil {
|
||||
return nil, nil, nil, errors.New("invalid line table")
|
||||
}
|
||||
tab, err := gosym.NewTable(nil, lineTab)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
pkgSyms := make(map[Symbol]bool)
|
||||
for _, f := range tab.Funcs {
|
||||
if f.Func == nil {
|
||||
continue
|
||||
}
|
||||
pkgName, symName, err := parseName(f.Func.Sym)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
pkgSyms[Symbol{pkgName, symName}] = true
|
||||
|
||||
// Collect symbols that were inlined in f.
|
||||
it, err := lineTab.InlineTree(&f, value, base, r)
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("InlineTree: %v", err)
|
||||
}
|
||||
for _, ic := range it {
|
||||
pkgName, symName, err := parseName(&gosym.Sym{Name: ic.Name})
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
pkgSyms[Symbol{pkgName, symName}] = true
|
||||
}
|
||||
}
|
||||
|
||||
var syms []Symbol
|
||||
for ps := range pkgSyms {
|
||||
syms = append(syms, ps)
|
||||
}
|
||||
|
||||
return debugModulesToPackagesModules(bi.Deps), syms, bi, nil
|
||||
}
|
||||
|
||||
func parseName(s *gosym.Sym) (pkg, sym string, err error) {
|
||||
symName := s.BaseName()
|
||||
if r := s.ReceiverName(); r != "" {
|
||||
if strings.HasPrefix(r, "(*") {
|
||||
r = strings.Trim(r, "(*)")
|
||||
}
|
||||
symName = fmt.Sprintf("%s.%s", r, symName)
|
||||
}
|
||||
|
||||
pkgName := s.PackageName()
|
||||
if pkgName != "" {
|
||||
pkgName, err = url.PathUnescape(pkgName)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
}
|
||||
return pkgName, symName, nil
|
||||
}
|
||||
221
vendor/golang.org/x/vuln/internal/buildinfo/buildinfo.go
generated
vendored
Normal file
221
vendor/golang.org/x/vuln/internal/buildinfo/buildinfo.go
generated
vendored
Normal file
@@ -0,0 +1,221 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
//go:build go1.18
|
||||
// +build go1.18
|
||||
|
||||
package buildinfo
|
||||
|
||||
// Addition: this file is a trimmed and slightly modified version of debug/buildinfo/buildinfo.go
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"debug/elf"
|
||||
"debug/macho"
|
||||
"debug/pe"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
// "internal/xcoff"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Addition: modification of rawBuildInfo in the original file.
|
||||
// openExe returns reader r as an exe.
|
||||
func openExe(r io.ReaderAt) (exe, error) {
|
||||
data := make([]byte, 16)
|
||||
if _, err := r.ReadAt(data, 0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if bytes.HasPrefix(data, []byte("\x7FELF")) {
|
||||
e, err := elf.NewFile(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &elfExe{f: e}, nil
|
||||
}
|
||||
if bytes.HasPrefix(data, []byte("MZ")) {
|
||||
e, err := pe.NewFile(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &peExe{r: r, f: e}, nil
|
||||
}
|
||||
if bytes.HasPrefix(data, []byte("\xFE\xED\xFA")) || bytes.HasPrefix(data[1:], []byte("\xFA\xED\xFE")) {
|
||||
e, err := macho.NewFile(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &machoExe{f: e}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("unrecognized executable format")
|
||||
}
|
||||
|
||||
type exe interface {
|
||||
// ReadData reads and returns up to size byte starting at virtual address addr.
|
||||
ReadData(addr, size uint64) ([]byte, error)
|
||||
|
||||
// DataStart returns the virtual address of the segment or section that
|
||||
// should contain build information. This is either a specially named section
|
||||
// or the first writable non-zero data segment.
|
||||
DataStart() uint64
|
||||
|
||||
PCLNTab() ([]byte, uint64) // Addition: for constructing symbol table
|
||||
|
||||
SymbolInfo(name string) (uint64, uint64, io.ReaderAt, error) // Addition: for inlining purposes
|
||||
}
|
||||
|
||||
// elfExe is the ELF implementation of the exe interface.
|
||||
type elfExe struct {
|
||||
f *elf.File
|
||||
|
||||
symbols map[string]*elf.Symbol // Addition: symbols in the binary
|
||||
symbolsOnce sync.Once // Addition: for computing symbols
|
||||
symbolsErr error // Addition: error for computing symbols
|
||||
}
|
||||
|
||||
func (x *elfExe) ReadData(addr, size uint64) ([]byte, error) {
|
||||
for _, prog := range x.f.Progs {
|
||||
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") // Addition: custom error
|
||||
}
|
||||
|
||||
func (x *elfExe) DataStart() uint64 {
|
||||
for _, s := range x.f.Sections {
|
||||
if s.Name == ".go.buildinfo" {
|
||||
return s.Addr
|
||||
}
|
||||
}
|
||||
for _, p := range x.f.Progs {
|
||||
if p.Type == elf.PT_LOAD && p.Flags&(elf.PF_X|elf.PF_W) == elf.PF_W {
|
||||
return p.Vaddr
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// peExe is the PE (Windows Portable Executable) implementation of the exe interface.
|
||||
type peExe struct {
|
||||
r io.ReaderAt
|
||||
f *pe.File
|
||||
|
||||
symbols map[string]*pe.Symbol // Addition: symbols in the binary
|
||||
symbolsOnce sync.Once // Addition: for computing symbols
|
||||
symbolsErr error // Addition: error for computing symbols
|
||||
}
|
||||
|
||||
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) ReadData(addr, size uint64) ([]byte, error) {
|
||||
addr -= x.imageBase()
|
||||
for _, sect := range x.f.Sections {
|
||||
if uint64(sect.VirtualAddress) <= addr && addr <= uint64(sect.VirtualAddress+sect.Size-1) {
|
||||
n := uint64(sect.VirtualAddress+sect.Size) - addr
|
||||
if n > size {
|
||||
n = size
|
||||
}
|
||||
data := make([]byte, n)
|
||||
_, 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") // Addition: custom error
|
||||
}
|
||||
|
||||
func (x *peExe) DataStart() uint64 {
|
||||
// Assume data is first writable section.
|
||||
const (
|
||||
IMAGE_SCN_CNT_CODE = 0x00000020
|
||||
IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040
|
||||
IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080
|
||||
IMAGE_SCN_MEM_EXECUTE = 0x20000000
|
||||
IMAGE_SCN_MEM_READ = 0x40000000
|
||||
IMAGE_SCN_MEM_WRITE = 0x80000000
|
||||
IMAGE_SCN_MEM_DISCARDABLE = 0x2000000
|
||||
IMAGE_SCN_LNK_NRELOC_OVFL = 0x1000000
|
||||
IMAGE_SCN_ALIGN_32BYTES = 0x600000
|
||||
)
|
||||
for _, sect := range x.f.Sections {
|
||||
if sect.VirtualAddress != 0 && sect.Size != 0 &&
|
||||
sect.Characteristics&^IMAGE_SCN_ALIGN_32BYTES == IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE {
|
||||
return uint64(sect.VirtualAddress) + x.imageBase()
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// machoExe is the Mach-O (Apple macOS/iOS) implementation of the exe interface.
|
||||
type machoExe struct {
|
||||
f *macho.File
|
||||
|
||||
symbols map[string]*macho.Symbol // Addition: symbols in the binary
|
||||
symbolsOnce sync.Once // Addition: for computing symbols
|
||||
symbolsErr error // Addition: error for computing symbols
|
||||
}
|
||||
|
||||
func (x *machoExe) ReadData(addr, size uint64) ([]byte, error) {
|
||||
for _, load := range x.f.Loads {
|
||||
seg, ok := load.(*macho.Segment)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if seg.Addr <= addr && addr <= seg.Addr+seg.Filesz-1 {
|
||||
if seg.Name == "__PAGEZERO" {
|
||||
continue
|
||||
}
|
||||
n := seg.Addr + seg.Filesz - addr
|
||||
if n > size {
|
||||
n = size
|
||||
}
|
||||
data := make([]byte, n)
|
||||
_, err := seg.ReadAt(data, int64(addr-seg.Addr))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("address not mapped") // Addition: custom error
|
||||
}
|
||||
|
||||
func (x *machoExe) DataStart() uint64 {
|
||||
// Look for section named "__go_buildinfo".
|
||||
for _, sec := range x.f.Sections {
|
||||
if sec.Name == "__go_buildinfo" {
|
||||
return sec.Addr
|
||||
}
|
||||
}
|
||||
// Try the first non-empty writable segment.
|
||||
const RW = 3
|
||||
for _, load := range x.f.Loads {
|
||||
seg, ok := load.(*macho.Segment)
|
||||
if ok && seg.Addr != 0 && seg.Filesz != 0 && seg.Prot == RW && seg.Maxprot == RW {
|
||||
return seg.Addr
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
Reference in New Issue
Block a user