283 lines
8.4 KiB
Text
Executable file
283 lines
8.4 KiB
Text
Executable file
#!/usr/bin/env newlisp
|
|
|
|
; sniff v.1.23 - net work packet snifffer
|
|
; 2010-March-26 added out-commented callback method
|
|
; 2010-April-10 documentation
|
|
; 2010-April-27 added 'packet' output format for whole packet
|
|
; added output of unknown packets
|
|
; 2010-April-29 help text improvements
|
|
; 2010-May-29 added library path for OpenBSD 4.6
|
|
; 2013-Sep-22 change stats to pstats (stats is function since 10.4.2)
|
|
;
|
|
; See also http://www.tcpdump.org/pcap.htm
|
|
; tested on Mac OS X 10.6 and UBUNTU 9.4 and Win32
|
|
; for a Win32 library goto http://www.winpcap.org/
|
|
; first version L.M.in March 2010
|
|
;
|
|
; This script must be run with super user prigileges.
|
|
|
|
(unless (main-args 2)
|
|
(println [text]
|
|
- sniff - network packet sniffer, version 1.21, April 2010
|
|
|
|
USAGE UNIX:
|
|
sudo sniff <device> [<filter> [<count> [hex | ascii | packet]]]
|
|
|
|
USAGE Win32:
|
|
newlisp sniff <device> [<filter> [<count> [hex | ascii | packet]]]
|
|
|
|
The program must run in super user or administrator mode. On Unix the
|
|
sudo utility can be used, and the OS will ask for the root password.
|
|
|
|
EXAMPLES:
|
|
sniff eth01 'tcp port 80' 100 hex
|
|
sniff en1 ip 100 packet <-- best all purpose format
|
|
sniff eth0 'ip host 10.1.2.3'
|
|
sniff en0
|
|
sniff ""
|
|
|
|
Use an empty string "" or '' for <device> to let sniff find out a device.
|
|
This works well on Windows but frequently does not work on UNIX, where
|
|
it is better to use the ifconfig utility to find a connected device.
|
|
|
|
The 'hex' option dumps data in HEX and ASCII, the 'ascii' option only in
|
|
ASCII format. Both options only dump the data part of the packet after the
|
|
TCP, UDP or frst 8 bytees of the ICMP header. The 'packet' option dumps
|
|
the whole packet starting with the IP header and in HEX and ASCII format.
|
|
|
|
Default <filter> expression is 'ip' for all protocols. The protocol ids:
|
|
tcp, udp and icmp are supported. Default packet <count> is 10 and
|
|
default output mode is no output.
|
|
[/text])
|
|
(exit)
|
|
)
|
|
|
|
; import pcap library
|
|
|
|
(set 'bit-64 (= 256 (& (sys-info -1) 256)))
|
|
|
|
(set 'files '(
|
|
"/usr/lib/libpcap.dylib" ; Mac OS X 10.6
|
|
"/usr/lib/libpcap.so.0.8" ; UBUNTU 9.04
|
|
"/usr/lib/libpcap.so.1.0.0"; UBUNTU 9.04
|
|
"/usr/lib/libpcap.so.6.0" ; OpenBSD 4.8
|
|
"c:/windows/system32/wpcap.dll" ; Win32 XP
|
|
))
|
|
|
|
(set 'libpcap (files (or
|
|
(find true (map file? files))
|
|
(throw-error "cannot find pcap packet capture library"))))
|
|
|
|
; import library functions
|
|
(import libpcap "pcap_lookupdev")
|
|
(import libpcap "pcap_lookupnet")
|
|
(import libpcap "pcap_open_live")
|
|
(import libpcap "pcap_compile")
|
|
(import libpcap "pcap_setfilter")
|
|
(import libpcap "pcap_next")
|
|
(import libpcap "pcap_loop")
|
|
(import libpcap "pcap_stats")
|
|
|
|
; allocate ptr varables used in library API
|
|
(set 'errbuf (dup "\000" 256)) ; for error message
|
|
(set 'fp (dup "\000" 8)) ; sizeof pointer (32 and 64 bit)
|
|
(set 'net "\000\000\000\000") ; IP number of sniffing device
|
|
(set 'mask "\000\000\000\000") ; net mask of sniffing device
|
|
(set 'header (dup "\000" 24)) ; space for packet header (32 and 64 bit)
|
|
(set 'protocols '((6 TCP) (17 UDP) (1 ICMP) (0 IP)))
|
|
|
|
; constants
|
|
(constant 'PCAP_BUFFSIZE 1518)
|
|
(constant 'PROMISCUOUS 1)
|
|
(constant 'SIZE_ETHER 14)
|
|
|
|
; get device
|
|
(set 'dev (main-args 2))
|
|
(when (= dev "")
|
|
(set 'dev (pcap_lookupdev errbuf)))
|
|
|
|
(when (zero? dev)
|
|
(println errbuf)
|
|
(println "must exit")
|
|
(exit 1))
|
|
|
|
(when (= (pcap_lookupnet dev net mask errbuf) -1)
|
|
(println (get-string errbuf))
|
|
(println "must exit")
|
|
(exit 1))
|
|
|
|
; get filter expression
|
|
(if (main-args 3)
|
|
(set 'filter-exp (lower-case (main-args 3)))
|
|
(set 'filter-exp "ip")) ; default is all packets
|
|
|
|
; get packet count
|
|
(set 'packet-count (int (main-args 4) 10)) ; default 10
|
|
|
|
; get output options
|
|
(set 'out-opt (or (main-args 5) "none"))
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
(set 'handle (pcap_open_live dev PCAP_BUFFSIZE PROMISCUOUS 1000 errbuf))
|
|
(when (= handle 0)
|
|
(println (get-string errbuf))
|
|
(println "must exit")
|
|
(exit 2))
|
|
|
|
(when (= (pcap_compile handle fp filter-exp 0 net) -1)
|
|
(println "could not compile filter:" filter-exp)
|
|
(println "must exit")
|
|
(exit 3))
|
|
|
|
(when (= (pcap_setfilter handle fp) -1)
|
|
(println "could not install filter:" filter-exp)
|
|
(println "must exit")
|
|
(exit 4))
|
|
|
|
; transform ip number to n.n.n.n
|
|
(define (ip-to-ascii ip)
|
|
(join (map string (unpack "bbbb" (pack ">lu" ip))) "."))
|
|
|
|
; tansform mac address to n:n::n:n:n:n
|
|
(define (eth-to-ascii host)
|
|
(join (map (fn (x) (format "%02x" x)) (unpack "bbbbbb" host) ) ":"))
|
|
|
|
; packet handler, called when packet arrives
|
|
(define (report-packet header packet)
|
|
|
|
(if bit-64
|
|
(map set '(tm usec dummy caplen len) (unpack "Lu lu lu lu lu" header))
|
|
(map set '(tm usec caplen len) (unpack "lu lu lu lu" header)))
|
|
(println "time: " (date tm 0 "%H:%M:%S.") usec " captured:" caplen " wire:" len)
|
|
|
|
; get pcap header
|
|
(map set '(dhost shost type) (unpack "s6 s6 d" packet))
|
|
(println "from ether addr: " (eth-to-ascii shost)
|
|
" to: " (eth-to-ascii dhost))
|
|
|
|
; IP header at p + SIZE_ETHER: sniff_ip
|
|
(map set '(vhl tos ip-len id off ttl proto sum src dest)
|
|
(unpack "> b b u u u b b u lu lu" (+ packet SIZE_ETHER)))
|
|
(set 'IP_HL (& vhl 0x0f))
|
|
(set 'IP_V (>> vhl 4))
|
|
|
|
(when (or (< IP_HL 5) (< ip-len (* IP_HL 4)))
|
|
(println "ERROR malformed IP datagram")
|
|
(throw true))
|
|
|
|
(set 'protocol (lookup proto protocols))
|
|
|
|
(if ; with multiple <condition> <body> terms
|
|
; TCP header: sniff_tcp
|
|
(= protocol 'TCP) ; TCP 6
|
|
(begin
|
|
(map set '(sport dport seq ack offx2)
|
|
(unpack "> u u lu lu b" (+ packet SIZE_ETHER (* IP_HL 4))))
|
|
(set 'TH_OFF (>> (& offx2 0xf0) 4)) ; * 4 -> offset
|
|
(when (< TH_OFF 5)
|
|
(println "ERROR malformed TCP segment")
|
|
(throw true))
|
|
|
|
(println protocol " from ip addr: " (ip-to-ascii src) " port:" sport
|
|
" to: " (ip-to-ascii dest) " port:" dport)
|
|
)
|
|
|
|
(= protocol 'UDP) ; UDP 17
|
|
(begin
|
|
(map set '(sport dport ulen sum)
|
|
(unpack "> u u u u" (+ packet SIZE_ETHER (* IP_HL 4))))
|
|
(set 'TH_OFF 2) ; 2*4=8 bytes of UDP header length
|
|
|
|
(println protocol " from " (ip-to-ascii src) " port:" sport
|
|
" to " (ip-to-ascii dest) " port:" dport)
|
|
)
|
|
|
|
(= protocol 'ICMP) ; ICMP 1
|
|
(begin
|
|
(map set '(type code cksum other)
|
|
(unpack "> b b u lu" (+ packet SIZE_ETHER (* IP_HL 4))))
|
|
(set 'TH_OFF 2) ; 2*4=8 bytes of ICMP header length
|
|
(println protocol
|
|
" from: " (ip-to-ascii src) " to: " (ip-to-ascii dest))
|
|
(println "type: " type " code: " code " checksum: " cksum)
|
|
)
|
|
|
|
true ; catch all other
|
|
(begin
|
|
(set 'TH_OFF 0)
|
|
(print "Protocol: " proto
|
|
" from: " (ip-to-ascii src) " to: " (ip-to-ascii dest)) )
|
|
) ; end if
|
|
|
|
; dump payload
|
|
;(set 'ip-len (min len (- caplen SIZE_ETHER)))
|
|
(set 'payload-len (- ip-len (* IP_HL 4) (* TH_OFF 4)))
|
|
(print "packet total len w/o ether-header:" ip-len)
|
|
(println ", payload length:" payload-len)
|
|
|
|
(when (and (zero? payload-len) (!= out-opt "packet"))
|
|
(throw true))
|
|
|
|
(if (= out-opt "packet")
|
|
(set 'data (unpack (dup "b" ip-len)
|
|
(+ packet SIZE_ETHER )))
|
|
(set 'data (unpack (dup "b" payload-len)
|
|
(+ packet SIZE_ETHER (* IP_HL 4) (* TH_OFF 4))))
|
|
)
|
|
|
|
; dump data as hex and ascii
|
|
(when (or (= out-opt "hex") (= out-opt "packet"))
|
|
(set 'addr 0)
|
|
(dolist (line (explode data 16) )
|
|
(print (format "%05d " addr))
|
|
(println (format "%-48s" (join (map (fn (b) (format "%02x " b)) line))) " "
|
|
(join (map (fn (c) (if (or (< c 32) (> c 126)) "." (char c))) line)))
|
|
(inc addr 16))
|
|
)
|
|
|
|
; dump ascii only
|
|
(when (= out-opt "ascii")
|
|
(dolist (line (explode data 64) )
|
|
(println (join (map (fn (c) (if (or (< c 32) (> c 126)) "." (char c))) line)))
|
|
)
|
|
)
|
|
|
|
true
|
|
)
|
|
|
|
(set 'counter 0)
|
|
(println "device: " (get-string dev))
|
|
|
|
; Method using pcap_next and newLISP controlling
|
|
; this method leves more time in newLISP to do
|
|
; stuff in the while loop.
|
|
(while (< counter packet-count)
|
|
(set 'capture '())
|
|
(set 'packet (pcap_next handle header))
|
|
(unless (= packet 0)
|
|
(print "\n--- " (inc counter) "--- ")
|
|
(catch (report-packet header packet ))
|
|
))
|
|
|
|
; Alternative method using callback with pcap_loop
|
|
; and pcap library controlling. This leaves less
|
|
; time for newLISP as the waiting for new packets
|
|
; happens inside libpcap
|
|
;(define (callback-handler params header packet)
|
|
; (unless (= packet 0)
|
|
; (print "\n--- " (inc counter) "--- ")
|
|
; (catch (report-packet header packet ))
|
|
;))
|
|
;(pcap_loop handle 20 (callback 0 'callback-handler) 0)
|
|
|
|
|
|
; report pcap status
|
|
(set 'pstats (dup "\000" 8))
|
|
(pcap_stats handle pstats)
|
|
(map set '(received dropped) (unpack "lu lu" pstats))
|
|
(println "\npackages dropped: " dropped)
|
|
|
|
(exit)
|
|
|
|
; eof
|