-
Notifications
You must be signed in to change notification settings - Fork 27
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add syn scanner example, update generated files.
- Loading branch information
Showing
5 changed files
with
374 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,259 @@ | ||
// Copyright 2012 Google, Inc. All rights reserved. | ||
// | ||
// Use of this source code is governed by a BSD-style license | ||
// that can be found in the LICENSE file in the root of the source | ||
// tree. | ||
|
||
// synscan implements a TCP syn scanner on top of pcap. | ||
// It's more complicated than arpscan, since it has to handle sending packets | ||
// outside the local network, requiring some routing and ARP work. | ||
// | ||
// Since this is just an example program, it aims for simplicity over | ||
// performance. It doesn't handle sending packets very quickly, it scans IPs | ||
// serially instead of in parallel, and uses gopacket.Packet instead of | ||
// gopacket.DecodingLayerParser for packet processing. We also make use of very | ||
// simple timeout logic with time.Since. | ||
// | ||
// Making it blazingly fast is left as an exercise to the reader. | ||
package main | ||
|
||
import ( | ||
"bytes" | ||
"flag" | ||
"fmt" | ||
"log" | ||
"net" | ||
"time" | ||
|
||
"code.google.com/p/gopacket" | ||
"code.google.com/p/gopacket/layers" | ||
"code.google.com/p/gopacket/pcap" | ||
"code.google.com/p/gopacket/routing" | ||
) | ||
|
||
// scanner handles scanning a single IP address. | ||
type scanner struct { | ||
// iface is the interface to send packets on. | ||
iface *net.Interface | ||
// destination, gateway (if applicable), and soruce IP addresses to use. | ||
dst, gw, src net.IP | ||
|
||
handle *pcap.Handle | ||
|
||
// opts and buf allow us to easily serialize packets in the send() | ||
// method. | ||
opts gopacket.SerializeOptions | ||
buf gopacket.SerializeBuffer | ||
} | ||
|
||
// newScanner creates a new scanner for a given destination IP address, using | ||
// router to determine how to route packets to that IP. | ||
func newScanner(ip net.IP, router routing.Router) (*scanner, error) { | ||
s := &scanner{ | ||
dst: ip, | ||
opts: gopacket.SerializeOptions{ | ||
FixLengths: true, | ||
ComputeChecksums: true, | ||
}, | ||
buf: gopacket.NewSerializeBuffer(), | ||
} | ||
// Figure out the route to the IP. | ||
iface, gw, src, err := router.Route(ip) | ||
if err != nil { | ||
return nil, err | ||
} | ||
log.Printf("scanning ip %v with interface %v, gateway %v, src %v", ip, iface.Name, gw, src) | ||
s.gw, s.src, s.iface = gw, src, iface | ||
|
||
// Open the handle for reading/writing. | ||
// Note we could very easily add some BPF filtering here to greatly | ||
// decrease the number of packets we have to look at when getting back | ||
// scan results. | ||
handle, err := pcap.OpenLive(iface.Name, 65536, true, time.Millisecond) | ||
if err != nil { | ||
return nil, err | ||
} | ||
s.handle = handle | ||
return s, nil | ||
} | ||
|
||
// close cleans up the handle. | ||
func (s *scanner) close() { | ||
s.handle.Close() | ||
} | ||
|
||
// getHwAddr is a hacky but effective way to get the destination hardware | ||
// address for our packets. It does an ARP request for our gateway (if there is | ||
// one) or destination IP (if no gateway is necessary), then waits for an ARP | ||
// reply. This is pretty slow right now, since it blocks on the ARP | ||
// request/reply. | ||
func (s *scanner) getHwAddr() (net.HardwareAddr, error) { | ||
start := time.Now() | ||
arpDst := s.dst | ||
if s.gw != nil { | ||
arpDst = s.gw | ||
} | ||
// Prepare the layers to send for an ARP request. | ||
eth := layers.Ethernet{ | ||
SrcMAC: s.iface.HardwareAddr, | ||
DstMAC: net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, | ||
EthernetType: layers.EthernetTypeARP, | ||
} | ||
arp := layers.ARP{ | ||
AddrType: layers.LinkTypeEthernet, | ||
Protocol: layers.EthernetTypeIPv4, | ||
HwAddressSize: 6, | ||
ProtAddressSize: 4, | ||
Operation: layers.ARPRequest, | ||
SourceHwAddress: []byte(s.iface.HardwareAddr), | ||
SourceProtAddress: []byte(s.src), | ||
DstHwAddress: []byte{0, 0, 0, 0, 0, 0}, | ||
DstProtAddress: []byte(arpDst), | ||
} | ||
// Send a single ARP request packet (we never retry a send, since this | ||
// is just an example ;) | ||
if err := s.send(ð, &arp); err != nil { | ||
return nil, err | ||
} | ||
// Wait 3 seconds for an ARP reply. | ||
for { | ||
if time.Since(start) > time.Second*3 { | ||
return nil, fmt.Errorf("timeout getting ARP reply") | ||
} | ||
data, _, err := s.handle.ReadPacketData() | ||
if err == pcap.NextErrorTimeoutExpired { | ||
continue | ||
} else if err != nil { | ||
return nil, err | ||
} | ||
packet := gopacket.NewPacket(data, layers.LayerTypeEthernet, gopacket.NoCopy) | ||
if arpLayer := packet.Layer(layers.LayerTypeARP); arpLayer != nil { | ||
arp := arpLayer.(*layers.ARP) | ||
if bytes.Equal(arp.SourceProtAddress, arpDst) { | ||
return net.HardwareAddr(arp.SourceHwAddress), nil | ||
} | ||
} | ||
} | ||
} | ||
|
||
// scan scans the dst IP address of this scanner. | ||
func (s *scanner) scan() error { | ||
// First off, get the MAC address we should be sending packets to. | ||
hwaddr, err := s.getHwAddr() | ||
if err != nil { | ||
return err | ||
} | ||
// Construct all the network layers we need. | ||
eth := layers.Ethernet{ | ||
SrcMAC: s.iface.HardwareAddr, | ||
DstMAC: hwaddr, | ||
EthernetType: layers.EthernetTypeIPv4, | ||
} | ||
ip4 := layers.IPv4{ | ||
SrcIP: s.src, | ||
DstIP: s.dst, | ||
Version: 4, | ||
TTL: 64, | ||
Protocol: layers.IPProtocolTCP, | ||
} | ||
tcp := layers.TCP{ | ||
SrcPort: 54321, | ||
DstPort: 0, // will be incremented during the scan | ||
SYN: true, | ||
} | ||
tcp.SetNetworkLayerForChecksum(&ip4) | ||
|
||
// Create the flow we expect returning packets to have, so we can check | ||
// against it and discard useless packets. | ||
ipFlow := gopacket.NewFlow(layers.EndpointIPv4, s.dst, s.src) | ||
start := time.Now() | ||
for { | ||
// Send one packet per loop iteration until we've sent packets | ||
// to all of ports [1, 65535]. | ||
if tcp.DstPort < 65535 { | ||
start = time.Now() | ||
tcp.DstPort++ | ||
if err := s.send(ð, &ip4, &tcp); err != nil { | ||
log.Printf("error sending to port %v: %v", tcp.DstPort, err) | ||
} | ||
} | ||
// Time out 5 seconds after the last packet we sent. | ||
if time.Since(start) > time.Second*5 { | ||
log.Printf("timed out for %v, assuming we've seen all we can", s.dst) | ||
return nil | ||
} | ||
|
||
// Read in the next packet. | ||
data, _, err := s.handle.ReadPacketData() | ||
if err == pcap.NextErrorTimeoutExpired { | ||
continue | ||
} else if err != nil { | ||
log.Printf("error reading packet: %v", err) | ||
continue | ||
} | ||
|
||
// Parse the packet. We'd use DecodingLayerParser here if we | ||
// wanted to be really fast. | ||
packet := gopacket.NewPacket(data, layers.LayerTypeEthernet, gopacket.NoCopy) | ||
|
||
// Find the packets we care about, and print out logging | ||
// information about them. All others are ignored. | ||
if net := packet.NetworkLayer(); net == nil { | ||
// log.Printf("packet has no network layer") | ||
} else if net.NetworkFlow() != ipFlow { | ||
// log.Printf("packet does not match our ip src/dst") | ||
} else if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer == nil { | ||
// log.Printf("packet has not tcp layer") | ||
} else if tcp, ok := tcpLayer.(*layers.TCP); !ok { | ||
// We panic here because this is guaranteed to never | ||
// happen. | ||
panic("tcp layer is not tcp layer :-/") | ||
} else if tcp.DstPort != 54321 { | ||
// log.Printf("dst port %v does not match", tcp.DstPort) | ||
} else if tcp.RST { | ||
log.Printf(" port %v closed", tcp.SrcPort) | ||
} else if tcp.SYN && tcp.ACK { | ||
log.Printf(" port %v open", tcp.SrcPort) | ||
} else { | ||
// log.Printf("ignoring useless packet") | ||
} | ||
} | ||
} | ||
|
||
// send sends the given layers as a single packet on the network. | ||
func (s *scanner) send(l ...gopacket.SerializableLayer) error { | ||
if err := gopacket.SerializeLayers(s.buf, s.opts, l...); err != nil { | ||
return err | ||
} | ||
return s.handle.WritePacketData(s.buf.Bytes()) | ||
} | ||
|
||
func main() { | ||
flag.Parse() | ||
router, err := routing.New() | ||
if err != nil { | ||
log.Fatal("routing error:", err) | ||
} | ||
for _, arg := range flag.Args() { | ||
var ip net.IP | ||
if ip = net.ParseIP(arg); ip == nil { | ||
log.Printf("non-ip target: %q", arg) | ||
continue | ||
} else if ip = ip.To4(); ip == nil { | ||
log.Printf("non-ipv4 target: %q", arg) | ||
continue | ||
} | ||
// Note: newScanner creates and closes a pcap Handle once for | ||
// every scan target. We could do much better, were this not an | ||
// example ;) | ||
s, err := newScanner(ip, router) | ||
if err != nil { | ||
log.Printf("unable to create scanner for %v: %v", ip, err) | ||
continue | ||
} | ||
if err := s.scan(); err != nil { | ||
log.Printf("unable to scan %v: %v", ip, err) | ||
} | ||
s.close() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.