Browse Source

lifted the modified go-netstat from BishopFox/sliver

40cb216d5f/implant/sliver/netstat
main
forest 2 months ago
commit
c24955913f
8 changed files with 1469 additions and 0 deletions
  1. +21
    -0
      LICENSE
  2. +60
    -0
      README.md
  3. +98
    -0
      netstat.go
  4. +154
    -0
      netstat_darwin.go
  5. +311
    -0
      netstat_linux.go
  6. +148
    -0
      netstat_types_darwin.go
  7. +587
    -0
      netstat_windows.go
  8. +90
    -0
      types_darwin.go

+ 21
- 0
LICENSE View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 Cihangir Akturk
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

+ 60
- 0
README.md View File

@ -0,0 +1,60 @@
## go-netstat / Sliver
A modified version of https://github.com/cakturk/go-netstat, modifications have been made for interoperability with the rest of [Silver](https://github.com/bishopfox/sliver):
* Added Darwin support
* Data structures have been ported to protobuf
### Using as a library
#### [Godoc](https://godoc.org/github.com/cakturk/go-netstat/netstat)
#### Getting the package
```
$ go get github.com/cakturk/go-netstat/netstat
```
```go
import (
"fmt"
"github.com/cakturk/go-netstat/netstat"
)
func displaySocks() error {
// UDP sockets
socks, err := netstat.UDPSocks(netstat.NoopFilter)
if err != nil {
return err
}
for _, e := range socks {
fmt.Printf("%v\n", e)
}
// TCP sockets
socks, err = netstat.TCPSocks(netstat.NoopFilter)
if err != nil {
return err
}
for _, e := range socks {
fmt.Printf("%v\n", e)
}
// get only listening TCP sockets
tabs, err = netstat.TCPSocks(func(s *netstat.SockTabEntry) bool {
return s.State == netstat.Listen
})
if err != nil {
return err
}
for _, e := range socks {
fmt.Printf("%v\n", e)
}
// list all the TCP sockets in state FIN_WAIT_1 for your HTTP server
tabs, err = netstat.TCPSocks(func(s *netstat.SockTabEntry) bool {
return s.State == netstat.FinWait1 && s.LocalAddr.Port == 80
})
// error handling, etc.
return nil
}
```

+ 98
- 0
netstat.go View File

@ -0,0 +1,98 @@
package netstat
/*
MIT License
Copyright (c) 2018 Cihangir Akturk
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
import (
"fmt"
"net"
)
// SockAddr represents an ip:port pair
type SockAddr struct {
IP net.IP
Port uint16
}
func (s *SockAddr) String() string {
return fmt.Sprintf("%v:%d", s.IP, s.Port)
}
// SockTabEntry type represents each line of the /proc/net/[tcp|udp]
type SockTabEntry struct {
ino string
LocalAddr *SockAddr
RemoteAddr *SockAddr
State SkState
UID uint32
Process *Process
}
// Process holds the PID and process name to which each socket belongs
type Process struct {
Pid int
Name string
}
func (p *Process) String() string {
return fmt.Sprintf("%d/%s", p.Pid, p.Name)
}
// SkState type represents socket connection state
type SkState uint8
func (s SkState) String() string {
return skStates[s]
}
// AcceptFn is used to filter socket entries. The value returned indicates
// whether the element is to be appended to the socket list.
type AcceptFn func(*SockTabEntry) bool
// NoopFilter - a test function returning true for all elements
func NoopFilter(*SockTabEntry) bool { return true }
// TCPSocks returns a slice of active TCP sockets containing only those
// elements that satisfy the accept function
func TCPSocks(accept AcceptFn) ([]SockTabEntry, error) {
return osTCPSocks(accept)
}
// TCP6Socks returns a slice of active TCP IPv4 sockets containing only those
// elements that satisfy the accept function
func TCP6Socks(accept AcceptFn) ([]SockTabEntry, error) {
return osTCP6Socks(accept)
}
// UDPSocks returns a slice of active UDP sockets containing only those
// elements that satisfy the accept function
func UDPSocks(accept AcceptFn) ([]SockTabEntry, error) {
return osUDPSocks(accept)
}
// UDP6Socks returns a slice of active UDP IPv6 sockets containing only those
// elements that satisfy the accept function
func UDP6Socks(accept AcceptFn) ([]SockTabEntry, error) {
return osUDP6Socks(accept)
}

+ 154
- 0
netstat_darwin.go View File

@ -0,0 +1,154 @@
package netstat
import (
"encoding/binary"
"net"
"unsafe"
"golang.org/x/sys/unix"
)
var skStates = [...]string{
"CLOSED",
"LISTEN",
"SYN_SENT",
"SYN_RCVD",
"ESTABLISHED",
"CLOSE_WAIT",
"FIN_WAIT_1",
"CLOSING",
"LAST_ACK",
"FIN_WAIT_2",
"TIME_WAIT",
}
// Socket states
const (
Closed SkState = 0 << iota
Listen
SynSent
SynRecvd
Established
CloseWait
FinWait1
Closing
LastAck
FinWait2
TimeWait
)
const (
TCP = 1 << iota
UDP
)
func osTCPSocks(accept AcceptFn) ([]SockTabEntry, error) {
return parseTCP(INP_IPV4)
}
func osTCP6Socks(accept AcceptFn) ([]SockTabEntry, error) {
return parseTCP(INP_IPV6)
}
func osUDPSocks(accept AcceptFn) ([]SockTabEntry, error) {
return parseUDP(INP_IPV4)
}
func osUDP6Socks(accept AcceptFn) ([]SockTabEntry, error) {
return parseUDP(INP_IPV6)
}
const (
INP_IPV4 = 1 << iota
INP_IPV6
)
func ntohs(v uint16) uint16 {
bs := make([]byte, 2)
binary.LittleEndian.PutUint16(bs, v)
return binary.BigEndian.Uint16(bs)
}
func getIP(data [16]byte, family int) net.IP {
var ip net.IP
switch family {
case INP_IPV4:
in4addr := (*InAddr4in6)(unsafe.Pointer(&data[0]))
ip = net.IP(in4addr.Addr4[:]).To4()
case INP_IPV6:
in6addr := (*In6Addr)(unsafe.Pointer(&data[0]))
ip = net.IP(in6addr.X__u6_addr[:])
}
return ip
}
func parseTCP(version uint8) ([]SockTabEntry, error) {
entries := make([]SockTabEntry, 0)
buf, err := unix.SysctlRaw("net.inet.tcp.pcblist64")
if err != nil {
return nil, err
}
xinpgen := (*Xinpgen)(unsafe.Pointer(&buf[0]))
current := xinpgen
for i := 1; i < int(xinpgen.Count); i++ {
xig := (*XTCPcb64)(unsafe.Pointer(current))
inp := (*Xinpcb64)(unsafe.Pointer(&xig.Pad_cgo_0))
if inp.Inp_vflag&version != 0 {
entry := parseXinpcb64(inp, TCP, version, xig)
entries = append(entries, entry)
}
current = (*Xinpgen)(unsafe.Pointer(uintptr(unsafe.Pointer(current)) + uintptr(current.Len)))
}
return entries, nil
}
func parseUDP(version uint8) ([]SockTabEntry, error) {
entries := make([]SockTabEntry, 0)
buf, err := unix.SysctlRaw("net.inet.udp.pcblist64")
if err != nil {
return nil, err
}
xinpgen := (*Xinpgen)(unsafe.Pointer(&buf[0]))
current := xinpgen
for i := 1; i < int(xinpgen.Count); i++ {
inp := (*Xinpcb64)(unsafe.Pointer(current))
if inp.Inp_vflag&version != 0 {
entry := parseXinpcb64(inp, UDP, version, nil)
entries = append(entries, entry)
}
current = (*Xinpgen)(unsafe.Pointer(uintptr(unsafe.Pointer(current)) + uintptr(current.Len)))
}
return entries, nil
}
func parseXinpcb64(inp *Xinpcb64, transport int, ipVersion uint8, xig *XTCPcb64) SockTabEntry {
var result SockTabEntry
lport := ntohs(inp.Inp_lport)
fport := ntohs(inp.Inp_fport)
switch ipVersion {
case INP_IPV4:
result.LocalAddr = &SockAddr{
IP: getIP(inp.Inp_dependladdr, INP_IPV4),
Port: lport,
}
result.RemoteAddr = &SockAddr{
IP: getIP(inp.Inp_dependfaddr, INP_IPV4),
Port: fport,
}
case INP_IPV6:
result.LocalAddr = &SockAddr{
IP: getIP(inp.Inp_dependladdr, INP_IPV6),
Port: lport,
}
result.RemoteAddr = &SockAddr{
IP: getIP(inp.Inp_dependfaddr, INP_IPV6),
Port: fport,
}
}
if xig != nil {
result.State = SkState(xig.T_state)
}
return result
}

+ 311
- 0
netstat_linux.go View File

@ -0,0 +1,311 @@
package netstat
/*
Package netstat provides primitives for getting socket information on a
Linux based operating system.
MIT License
Copyright (c) 2018 Cihangir Akturk
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
import (
"bufio"
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"io/ioutil"
"net"
"os"
"path"
"strconv"
"strings"
)
const (
pathTCPTab = "/proc/net/tcp"
pathTCP6Tab = "/proc/net/tcp6"
pathUDPTab = "/proc/net/udp"
pathUDP6Tab = "/proc/net/udp6"
ipv4StrLen = 8
ipv6StrLen = 32
)
// Socket states
const (
Established SkState = 0x01
SynSent = 0x02
SynRecv = 0x03
FinWait1 = 0x04
FinWait2 = 0x05
TimeWait = 0x06
Close = 0x07
CloseWait = 0x08
LastAck = 0x09
Listen = 0x0a
Closing = 0x0b
)
var skStates = [...]string{
"UNKNOWN",
"ESTABLISHED",
"SYN_SENT",
"SYN_RECV",
"FIN_WAIT1",
"FIN_WAIT2",
"TIME_WAIT",
"", // CLOSE
"CLOSE_WAIT",
"LAST_ACK",
"LISTEN",
"CLOSING",
}
// Errors returned by gonetstat
var (
ErrNotEnoughFields = errors.New("gonetstat: not enough fields in the line")
)
func parseIPv4(s string) (net.IP, error) {
v, err := strconv.ParseUint(s, 16, 32)
if err != nil {
return nil, err
}
ip := make(net.IP, net.IPv4len)
binary.LittleEndian.PutUint32(ip, uint32(v))
return ip, nil
}
func parseIPv6(s string) (net.IP, error) {
ip := make(net.IP, net.IPv6len)
const grpLen = 4
i, j := 0, 4
for len(s) != 0 {
grp := s[0:8]
u, err := strconv.ParseUint(grp, 16, 32)
binary.LittleEndian.PutUint32(ip[i:j], uint32(u))
if err != nil {
return nil, err
}
i, j = i+grpLen, j+grpLen
s = s[8:]
}
return ip, nil
}
func parseAddr(s string) (*SockAddr, error) {
fields := strings.Split(s, ":")
if len(fields) < 2 {
return nil, fmt.Errorf("netstat: not enough fields: %v", s)
}
var ip net.IP
var err error
switch len(fields[0]) {
case ipv4StrLen:
ip, err = parseIPv4(fields[0])
case ipv6StrLen:
ip, err = parseIPv6(fields[0])
default:
return nil, fmt.Errorf("Bad formatted string")
}
if err != nil {
return nil, err
}
v, err := strconv.ParseUint(fields[1], 16, 16)
if err != nil {
return nil, err
}
return &SockAddr{IP: ip, Port: uint16(v)}, nil
}
func parseSocktab(r io.Reader, accept AcceptFn) ([]SockTabEntry, error) {
br := bufio.NewScanner(r)
tab := make([]SockTabEntry, 0, 4)
// Discard title
br.Scan()
for br.Scan() {
var e SockTabEntry
line := br.Text()
// Skip comments
if i := strings.Index(line, "#"); i >= 0 {
line = line[:i]
}
fields := strings.Fields(line)
if len(fields) < 12 {
return nil, fmt.Errorf("netstat: not enough fields: %v, %v", len(fields), fields)
}
addr, err := parseAddr(fields[1])
if err != nil {
return nil, err
}
e.LocalAddr = addr
addr, err = parseAddr(fields[2])
if err != nil {
return nil, err
}
e.RemoteAddr = addr
u, err := strconv.ParseUint(fields[3], 16, 8)
if err != nil {
return nil, err
}
e.State = SkState(u)
u, err = strconv.ParseUint(fields[7], 10, 32)
if err != nil {
return nil, err
}
e.UID = uint32(u)
e.ino = fields[9]
if accept(&e) {
tab = append(tab, e)
}
}
return tab, br.Err()
}
type procFd struct {
base string
pid int
sktab []SockTabEntry
p *Process
}
var sockPrefix = "socket:["
func getProcName(s []byte) string {
i := bytes.Index(s, []byte("("))
if i < 0 {
return ""
}
j := bytes.LastIndex(s, []byte(")"))
if i < 0 {
return ""
}
if i > j {
return ""
}
return string(s[i+1 : j])
}
func (p *procFd) iterFdDir() {
// link name is of the form socket:[5860846]
fddir := path.Join(p.base, "/fd")
fi, err := ioutil.ReadDir(fddir)
if err != nil {
return
}
var buf [128]byte
for _, file := range fi {
fd := path.Join(fddir, file.Name())
lname, err := os.Readlink(fd)
if err != nil {
continue
}
for i := range p.sktab {
sk := &p.sktab[i]
ss := sockPrefix + sk.ino + "]"
if ss != lname {
continue
}
if p.p == nil {
stat, err := os.Open(path.Join(p.base, "stat"))
if err != nil {
return
}
n, err := stat.Read(buf[:])
stat.Close()
if err != nil {
return
}
z := bytes.SplitN(buf[:n], []byte(" "), 3)
name := getProcName(z[1])
p.p = &Process{p.pid, name}
}
sk.Process = p.p
}
}
}
func extractProcInfo(sktab []SockTabEntry) {
var basedir = "/proc"
fi, err := ioutil.ReadDir(basedir)
if err != nil {
return
}
for _, file := range fi {
if !file.IsDir() {
continue
}
pid, err := strconv.Atoi(file.Name())
if err != nil {
continue
}
base := path.Join(basedir, file.Name())
proc := procFd{base: base, pid: pid, sktab: sktab}
proc.iterFdDir()
}
}
// doNetstat - collect information about network port status
func doNetstat(path string, fn AcceptFn) ([]SockTabEntry, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
tabs, err := parseSocktab(f, fn)
f.Close()
if err != nil {
return nil, err
}
extractProcInfo(tabs)
return tabs, nil
}
// TCPSocks returns a slice of active TCP sockets containing only those
// elements that satisfy the accept function
func osTCPSocks(accept AcceptFn) ([]SockTabEntry, error) {
return doNetstat(pathTCPTab, accept)
}
// TCP6Socks returns a slice of active TCP IPv4 sockets containing only those
// elements that satisfy the accept function
func osTCP6Socks(accept AcceptFn) ([]SockTabEntry, error) {
return doNetstat(pathTCP6Tab, accept)
}
// UDPSocks returns a slice of active UDP sockets containing only those
// elements that satisfy the accept function
func osUDPSocks(accept AcceptFn) ([]SockTabEntry, error) {
return doNetstat(pathUDPTab, accept)
}
// UDP6Socks returns a slice of active UDP IPv6 sockets containing only those
// elements that satisfy the accept function
func osUDP6Socks(accept AcceptFn) ([]SockTabEntry, error) {
return doNetstat(pathUDP6Tab, accept)
}

+ 148
- 0
netstat_types_darwin.go View File

@ -0,0 +1,148 @@
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
// cgo -godefs /Users/lesnuages/code/go/src/github.com/lesnuages/netstat/netstat/types_darwin.go
package netstat
const (
sizeofPtr = 0x8
sizeofShort = 0x2
sizeofInt = 0x4
sizeofLong = 0x8
sizeofLongLong = 0x8
)
type (
_C_short int16
_C_int int32
_C_long int64
_C_long_long int64
)
type In6Addr struct {
X__u6_addr [16]byte
}
type InAddr4in6 struct {
Pad32 [3]uint32
Addr4 [4]byte /* in_addr */
}
type XSockbuf struct {
Cc uint32
Hiwat uint32
Mbcnt uint32
Mbmax uint32
Lowat int32
Flags int16
Timeo int16
}
type XSocket64 struct {
Xso_len uint32
Pad_cgo_0 [8]byte
So_type int16
So_options int16
So_linger int16
So_state int16
Pad_cgo_1 [8]byte
Xso_protocol int32
Xso_family int32
So_qlen int16
So_incqlen int16
So_qlimit int16
So_timeo int16
So_error uint16
So_pgid int32
So_oobmark uint32
So_rcv XSockbuf
So_snd XSockbuf
So_uid uint32
}
type Xinpgen struct {
Len uint32
Count uint32
Gen uint64
Sogen uint64
}
type InPCB64ListEntry struct {
Next uint64
Prev uint64
}
type Xinpcb64 struct {
Xi_len uint64
Xi_inpp uint64
Inp_fport uint16
Inp_lport uint16
Pad_cgo_0 [64]byte
Inp_flags int32
Inp_flow uint32
Inp_vflag uint8
Inp_ip_ttl uint8
Inp_ip_p uint8
Pad_cgo_1 [1]byte
Inp_dependfaddr [16]byte
Inp_dependladdr [16]byte
// Inp_depend4 _Ctype_struct___3
// Inp_depend6 _Ctype_struct___4
Xi_socket XSocket64
Pad_cgo_2 [8]byte
}
type XTCPcb64 struct {
Xt_len uint32
Pad_cgo_0 [256]byte
T_segq uint64
T_dupacks int32
T_timer [4]int32
T_state int32
T_flags uint32
T_force int32
Snd_una uint32
Snd_max uint32
Snd_nxt uint32
Snd_up uint32
Snd_wl1 uint32
Snd_wl2 uint32
Iss uint32
Irs uint32
Rcv_nxt uint32
Rcv_adv uint32
Rcv_wnd uint32
Rcv_up uint32
Snd_wnd uint32
Snd_cwnd uint32
Snd_ssthresh uint32
T_maxopd uint32
T_rcvtime uint32
T_starttime uint32
T_rtttime int32
T_rtseq uint32
T_rxtcur int32
T_maxseg uint32
T_srtt int32
T_rttvar int32
T_rxtshift int32
T_rttmin uint32
T_rttupdated uint32
Max_sndwnd uint32
T_softerror int32
T_oobflags int8
T_iobc int8
Snd_scale uint8
Rcv_scale uint8
Request_r_scale uint8
Requested_s_scale uint8
Ts_recent uint32
Ts_recent_age uint32
Last_ack_sent uint32
Cc_send uint32
Cc_recv uint32
Snd_recover uint32
Snd_cwnd_prev uint32
Snd_ssthresh_prev uint32
T_badrxtwin uint32
Xt_alignment_hack uint64
}

+ 587
- 0
netstat_windows.go View File

@ -0,0 +1,587 @@
/*
MIT License
Copyright (c) 2018 Cihangir Akturk
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
package netstat
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"net"
"reflect"
"syscall"
"unsafe"
)
const (
errInsuffBuff = syscall.Errno(122)
Th32csSnapProcess = uint32(0x00000002)
InvalidHandleValue = ^uintptr(0)
MaxPath = 260
)
var (
errNoMoreFiles = errors.New("no more files have been found")
modiphlpapi = syscall.NewLazyDLL("Iphlpapi.dll")
modkernel32 = syscall.NewLazyDLL("Kernel32.dll")
procGetTCPTable2 = modiphlpapi.NewProc("GetTcpTable2")
procGetTCP6Table2 = modiphlpapi.NewProc("GetTcp6Table2")
procGetExtendedUDPTable = modiphlpapi.NewProc("GetExtendedUdpTable")
procCreateSnapshot = modkernel32.NewProc("CreateToolhelp32Snapshot")
procProcess32First = modkernel32.NewProc("Process32First")
procProcess32Next = modkernel32.NewProc("Process32Next")
)
// Socket states
const (
Close SkState = 0x01
Listen = 0x02
SynSent = 0x03
SynRecv = 0x04
Established = 0x05
FinWait1 = 0x06
FinWait2 = 0x07
CloseWait = 0x08
Closing = 0x09
LastAck = 0x0a
TimeWait = 0x0b
DeleteTcb = 0x0c
)
var skStates = [...]string{
"UNKNOWN",
"", // CLOSE
"LISTEN",
"SYN_SENT",
"SYN_RECV",
"ESTABLISHED",
"FIN_WAIT1",
"FIN_WAIT2",
"CLOSE_WAIT",
"CLOSING",
"LAST_ACK",
"TIME_WAIT",
"DELETE_TCB",
}
func memToIPv4(p unsafe.Pointer) net.IP {
a := (*[net.IPv4len]byte)(p)
ip := make(net.IP, net.IPv4len)
copy(ip, a[:])
return ip
}
func memToIPv6(p unsafe.Pointer) net.IP {
a := (*[net.IPv6len]byte)(p)
ip := make(net.IP, net.IPv6len)
copy(ip, a[:])
return ip
}
func memtohs(n unsafe.Pointer) uint16 {
return binary.BigEndian.Uint16((*[2]byte)(n)[:])
}
type WinSock struct {
Addr uint32
Port uint32
}
func (w *WinSock) Sock() *SockAddr {
ip := memToIPv4(unsafe.Pointer(&w.Addr))
port := memtohs(unsafe.Pointer(&w.Port))
return &SockAddr{IP: ip, Port: port}
}
type WinSock6 struct {
Addr [net.IPv6len]byte
ScopeID uint32
Port uint32
}
func (w *WinSock6) Sock() *SockAddr {
ip := memToIPv6(unsafe.Pointer(&w.Addr[0]))
port := memtohs(unsafe.Pointer(&w.Port))
return &SockAddr{IP: ip, Port: port}
}
type MibTCPRow2 struct {
State uint32
LocalAddr WinSock
RemoteAddr WinSock
WinPid
OffloadState uint32
}
type WinPid uint32
func (pid WinPid) Process(snp ProcessSnapshot) *Process {
if pid < 1 {
return nil
}
return &Process{
Pid: int(pid),
Name: snp.ProcPIDToName(uint32(pid)),
}
}
func (m *MibTCPRow2) LocalSock() *SockAddr { return m.LocalAddr.Sock() }
func (m *MibTCPRow2) RemoteSock() *SockAddr { return m.RemoteAddr.Sock() }
func (m *MibTCPRow2) SockState() SkState { return SkState(m.State) }
type MibTCPTable2 struct {
NumEntries uint32
Table [1]MibTCPRow2
}
func (t *MibTCPTable2) Rows() []MibTCPRow2 {
var s []MibTCPRow2
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&s))
hdr.Data = uintptr(unsafe.Pointer(&t.Table[0]))
hdr.Len = int(t.NumEntries)
hdr.Cap = int(t.NumEntries)
return s
}
// MibTCP6Row2 structure contains information that describes an IPv6 TCP
// connection.
type MibTCP6Row2 struct {
LocalAddr WinSock6
RemoteAddr WinSock6
State uint32
WinPid
OffloadState uint32
}
func (m *MibTCP6Row2) LocalSock() *SockAddr { return m.LocalAddr.Sock() }
func (m *MibTCP6Row2) RemoteSock() *SockAddr { return m.RemoteAddr.Sock() }
func (m *MibTCP6Row2) SockState() SkState { return SkState(m.State) }
// MibTCP6Table2 structure contains a table of IPv6 TCP connections on the
// local computer.
type MibTCP6Table2 struct {
NumEntries uint32
Table [1]MibTCP6Row2
}
func (t *MibTCP6Table2) Rows() []MibTCP6Row2 {
var s []MibTCP6Row2
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&s))
hdr.Data = uintptr(unsafe.Pointer(&t.Table[0]))
hdr.Len = int(t.NumEntries)
hdr.Cap = int(t.NumEntries)
return s
}
// MibUDPRowOwnerPID structure contains an entry from the User Datagram
// Protocol (UDP) listener table for IPv4 on the local computer. The entry also
// includes the process ID (PID) that issued the call to the bind function for
// the UDP endpoint
type MibUDPRowOwnerPID struct {
WinSock
WinPid
}
func (m *MibUDPRowOwnerPID) LocalSock() *SockAddr { return m.Sock() }
func (m *MibUDPRowOwnerPID) RemoteSock() *SockAddr { return &SockAddr{net.IPv4zero, 0} }
func (m *MibUDPRowOwnerPID) SockState() SkState { return Close }
// MibUDPTableOwnerPID structure contains the User Datagram Protocol (UDP)
// listener table for IPv4 on the local computer. The table also includes the
// process ID (PID) that issued the call to the bind function for each UDP
// endpoint.
type MibUDPTableOwnerPID struct {
NumEntries uint32
Table [1]MibUDPRowOwnerPID
}
func (t *MibUDPTableOwnerPID) Rows() []MibUDPRowOwnerPID {
var s []MibUDPRowOwnerPID
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&s))
hdr.Data = uintptr(unsafe.Pointer(&t.Table[0]))
hdr.Len = int(t.NumEntries)
hdr.Cap = int(t.NumEntries)
return s
}
// MibUDP6RowOwnerPID serves the same purpose as MibUDPRowOwnerPID, except that
// the information in this case is for IPv6.
type MibUDP6RowOwnerPID struct {
WinSock6
WinPid
}
func (m *MibUDP6RowOwnerPID) LocalSock() *SockAddr { return m.Sock() }
func (m *MibUDP6RowOwnerPID) RemoteSock() *SockAddr { return &SockAddr{net.IPv4zero, 0} }
func (m *MibUDP6RowOwnerPID) SockState() SkState { return Close }
// MibUDP6TableOwnerPID serves the same purpose as MibUDPTableOwnerPID for IPv6
type MibUDP6TableOwnerPID struct {
NumEntries uint32
Table [1]MibUDP6RowOwnerPID
}
func (t *MibUDP6TableOwnerPID) Rows() []MibUDP6RowOwnerPID {
var s []MibUDP6RowOwnerPID
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&s))
hdr.Data = uintptr(unsafe.Pointer(&t.Table[0]))
hdr.Len = int(t.NumEntries)
hdr.Cap = int(t.NumEntries)
return s
}
// Processentry32 describes an entry from a list of the processes residing in
// the system address space when a snapshot was taken
type Processentry32 struct {
Size uint32
CntUsage uint32
Th32ProcessID uint32
Th32DefaultHeapID uintptr
Th32ModuleID uint32
CntThreads uint32
Th32ParentProcessID uint32
PriClassBase int32
Flags uint32
ExeFile [MaxPath]byte
}
func rawGetTCPTable2(proc uintptr, tab unsafe.Pointer, size *uint32, order bool) error {
var oint uintptr
if order {
oint = 1
}
r1, _, callErr := syscall.Syscall(
proc,
uintptr(3),
uintptr(tab),
uintptr(unsafe.Pointer(size)),
oint)
if callErr != 0 {
return callErr
}
if r1 != 0 {
return syscall.Errno(r1)
}
return nil
}
func getTCPTable2(proc uintptr, order bool) ([]byte, error) {
var (
size uint32
buf []byte
)
// determine size
err := rawGetTCPTable2(proc, unsafe.Pointer(nil), &size, false)
if err != nil && err != errInsuffBuff {
return nil, err
}
buf = make([]byte, size)
table := unsafe.Pointer(&buf[0])
err = rawGetTCPTable2(proc, table, &size, true)
if err != nil {
return nil, err
}
return buf, nil
}
// GetTCPTable2 function retrieves the IPv4 TCP connection table
func GetTCPTable2(order bool) (*MibTCPTable2, error) {
b, err := getTCPTable2(procGetTCPTable2.Addr(), true)
if err != nil {
return nil, err
}
return (*MibTCPTable2)(unsafe.Pointer(&b[0])), nil
}
// GetTCP6Table2 function retrieves the IPv6 TCP connection table
func GetTCP6Table2(order bool) (*MibTCP6Table2, error) {
b, err := getTCPTable2(procGetTCP6Table2.Addr(), true)
if err != nil {
return nil, err
}
return (*MibTCP6Table2)(unsafe.Pointer(&b[0])), nil
}
// The UDPTableClass enumeration defines the set of values used to indicate
// the type of table returned by calls to GetExtendedUDPTable
type UDPTableClass uint
// Possible table class values
const (
UDPTableBasic UDPTableClass = iota
UDPTableOwnerPID
UDPTableOwnerModule
)
func getExtendedUDPTable(table unsafe.Pointer, size *uint32, order bool, af uint32, cl UDPTableClass) error {
var oint uintptr
if order {
oint = 1
}
r1, _, callErr := syscall.Syscall6(
procGetExtendedUDPTable.Addr(),
uintptr(6),
uintptr(table),
uintptr(unsafe.Pointer(size)),
oint,
uintptr(af),
uintptr(cl),
uintptr(0))
if callErr != 0 {
return callErr
}
if r1 != 0 {
return syscall.Errno(r1)
}
return nil
}
// GetExtendedUDPTable function retrieves a table that contains a list of UDP
// endpoints available to the application
func GetExtendedUDPTable(order bool, af uint32, cl UDPTableClass) ([]byte, error) {
var size uint32
err := getExtendedUDPTable(nil, &size, order, af, cl)
if err != nil && err != errInsuffBuff {
return nil, err
}
buf := make([]byte, size)
err = getExtendedUDPTable(unsafe.Pointer(&buf[0]), &size, order, af, cl)
if err != nil {
return nil, err
}
return buf, nil
}
func GetUDPTableOwnerPID(order bool) (*MibUDPTableOwnerPID, error) {
b, err := GetExtendedUDPTable(true, syscall.AF_INET, UDPTableOwnerPID)
if err != nil {
return nil, err
}
return (*MibUDPTableOwnerPID)(unsafe.Pointer(&b[0])), nil
}
func GetUDP6TableOwnerPID(order bool) (*MibUDP6TableOwnerPID, error) {
b, err := GetExtendedUDPTable(true, syscall.AF_INET6, UDPTableOwnerPID)
if err != nil {
return nil, err
}
return (*MibUDP6TableOwnerPID)(unsafe.Pointer(&b[0])), nil
}
// ProcessSnapshot wraps the syscall.Handle, which represents a snapshot of
// the specified processes.
type ProcessSnapshot syscall.Handle
// CreateToolhelp32Snapshot takes a snapshot of the specified processes, as
// well as the heaps, modules, and threads used by these processes
func CreateToolhelp32Snapshot(flags uint32, pid uint32) (ProcessSnapshot, error) {
r1, _, callErr := syscall.Syscall(
procCreateSnapshot.Addr(),
uintptr(2),
uintptr(flags),
uintptr(pid), 0)
ret := ProcessSnapshot(r1)
if callErr != 0 {
return ret, callErr
}
if r1 == InvalidHandleValue {
return ret, fmt.Errorf("invalid handle value: %#v", r1)
}
return ret, nil
}
// ProcPIDToName translates PID to a name
func (snp ProcessSnapshot) ProcPIDToName(pid uint32) string {
var processEntry Processentry32
processEntry.Size = uint32(unsafe.Sizeof(processEntry))
handle := syscall.Handle(snp)
err := Process32First(handle, &processEntry)
if err != nil {
return ""
}
for {
if processEntry.Th32ProcessID == pid {
return StringFromNullTerminated(processEntry.ExeFile[:])
}
err = Process32Next(handle, &processEntry)
if err != nil {
return ""
}
}
}
// Close releases underlying win32 handle
func (snp ProcessSnapshot) Close() error {
return syscall.CloseHandle(syscall.Handle(snp))
}
// Process32First retrieves information about the first process encountered
// in a system snapshot
func Process32First(handle syscall.Handle, pe *Processentry32) error {
pe.Size = uint32(unsafe.Sizeof(*pe))
r1, _, callErr := syscall.Syscall(
procProcess32First.Addr(),
uintptr(2),
uintptr(handle),
uintptr(unsafe.Pointer(pe)), 0)
if callErr != 0 {
return callErr
}
if r1 == 0 {
return errNoMoreFiles
}
return nil
}
// Process32Next retrieves information about the next process
// recorded in a system snapshot
func Process32Next(handle syscall.Handle, pe *Processentry32) error {
pe.Size = uint32(unsafe.Sizeof(*pe))
r1, _, callErr := syscall.Syscall(
procProcess32Next.Addr(),
uintptr(2),
uintptr(handle),
uintptr(unsafe.Pointer(pe)), 0)
if callErr != 0 {
return callErr
}
if r1 == 0 {
return errNoMoreFiles
}
return nil
}
// StringFromNullTerminated returns a string from a nul-terminated byte slice
func StringFromNullTerminated(b []byte) string {
n := bytes.IndexByte(b, '\x00')
if n < 1 {
return ""
}
return string(b[:n])
}
type winSockEnt interface {
LocalSock() *SockAddr
RemoteSock() *SockAddr
SockState() SkState
Process(snp ProcessSnapshot) *Process
}
func toSockTabEntry(ws winSockEnt, snp ProcessSnapshot) SockTabEntry {
return SockTabEntry{
LocalAddr: ws.LocalSock(),
RemoteAddr: ws.RemoteSock(),
State: ws.SockState(),
Process: ws.Process(snp),
}
}
func osTCPSocks(accept AcceptFn) ([]SockTabEntry, error) {
tbl, err := GetTCPTable2(true)
if err != nil {
return nil, err
}
snp, err := CreateToolhelp32Snapshot(Th32csSnapProcess, 0)
if err != nil {
return nil, err
}
var sktab []SockTabEntry
s := tbl.Rows()
for i := range s {
ent := toSockTabEntry(&s[i], snp)
if accept(&ent) {
sktab = append(sktab, ent)
}
}
snp.Close()
return sktab, nil
}
func osTCP6Socks(accept AcceptFn) ([]SockTabEntry, error) {
tbl, err := GetTCP6Table2(true)
if err != nil {
return nil, err
}
snp, err := CreateToolhelp32Snapshot(Th32csSnapProcess, 0)
if err != nil {
return nil, err
}
var sktab []SockTabEntry
s := tbl.Rows()
for i := range s {
ent := toSockTabEntry(&s[i], snp)
if accept(&ent) {
sktab = append(sktab, ent)
}
}
snp.Close()
return sktab, nil
}
func osUDPSocks(accept AcceptFn) ([]SockTabEntry, error) {
tbl, err := GetUDPTableOwnerPID(true)
if err != nil {
return nil, err
}
snp, err := CreateToolhelp32Snapshot(Th32csSnapProcess, 0)
if err != nil {
return nil, err
}
var sktab []SockTabEntry
s := tbl.Rows()
for i := range s {
ent := toSockTabEntry(&s[i], snp)
if accept(&ent) {
sktab = append(sktab, ent)
}
}
snp.Close()
return sktab, nil
}
func osUDP6Socks(accept AcceptFn) ([]SockTabEntry, error) {
tbl, err := GetUDP6TableOwnerPID(true)
if err != nil {
return nil, err
}
snp, err := CreateToolhelp32Snapshot(Th32csSnapProcess, 0)
if err != nil {
return nil, err
}
var sktab []SockTabEntry
s := tbl.Rows()
for i := range s {
ent := toSockTabEntry(&s[i], snp)
if accept(&ent) {
sktab = append(sktab, ent)
}
}
snp.Close()
return sktab, nil
}

+ 90
- 0
types_darwin.go View File

@ -0,0 +1,90 @@
// +build ignore
/*
Input to cgo -godefs.
*/
// +godefs map struct_in_addr [4]byte /* in_addr */
// +godefs map struct_in6_addr [16]byte /* in6_addr */
// +godefs map struct_ [16]byte /* in6_addr */
package netstat
/*
#define __DARWIN_UNIX03 0
#define KERNEL 1
#define XNU_TARGET_OS_OSX 1
#define _DARWIN_USE_64_BIT_INODE
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/un.h>
#include <net/bpf.h>
#include <net/if_dl.h>
#include <net/if_var.h>
#include <net/route.h>
#include <netinet/in.h>
#include <sys/socketvar.h>
#include <netinet/in.h>
#include <netinet/in_pcb.h>
#include <netinet/tcp_var.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#define TCPSTATES
#include <netinet/tcp_fsm.h>
#include <netinet/tcp_seq.h>
enum {
sizeofPtr = sizeof(void*),
};
union sockaddr_all {
struct sockaddr s1; // this one gets used for fields
struct sockaddr_in s2; // these pad it out
struct sockaddr_in6 s3;
struct sockaddr_un s4;
struct sockaddr_dl s5;
};
struct sockaddr_any {
struct sockaddr addr;
char pad[sizeof(union sockaddr_all) - sizeof(struct sockaddr)];
};
*/
import "C"
// Machine characteristics; for internal use.
const (
sizeofPtr = C.sizeofPtr
sizeofShort = C.sizeof_short
sizeofInt = C.sizeof_int
sizeofLong = C.sizeof_long
sizeofLongLong = C.sizeof_longlong
)
// Basic types
type (
_C_short C.short
_C_int C.int
_C_long C.long
_C_long_long C.longlong
)
type In6Addr C.struct_in6_addr
type InAddr4in6 C.struct_in_addr_4in6
type XSockbuf C.struct_xsockbuf
type XSocket64 C.struct_xsocket64
type Xinpgen C.struct_xinpgen
type InPCB64ListEntry C.struct_inpcb64_list_entry
type Xinpcb64 C.struct_xinpcb64
type XTCPcb64 C.struct_xtcpcb64

Loading…
Cancel
Save