350 lines
8.1 KiB
Go
350 lines
8.1 KiB
Go
|
|
// 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
|
||
|
|
}
|