依赖的Perl第三方库在Ubuntu上安装:
- libnet-pcap-perl
- libnet-dns-perl
如果还需要其他Perl第三方库,可以通过apt-file(Ubuntu Lucid默认没有安装)来检索所需要的pm文件在哪个deb包。
例如:
$ apt-file search Net::DNS::Packet libnet-dns-perl: /usr/share/man/man3/Net::DNS::Packet.3pm.gz
$ apt-file search Net::Pcap libnet-pcap-perl: /usr/share/man/man3/Net::Pcap.3pm.gz
Wireshark虽然也可以抓包分析,但是用可编程的脚本语言来辅助分析可以省去很多肉眼比对的工作。
下面直接上代码:
#!/usr/bin/perl -w # # Copyright 2003, Brian Hatch, released under the GPL # # watch_dns: # A program to watch for inbound DNS queries, and print the # source, destination, and requested domain name of the queries. # # You'll need to fill this in with your actual IP address # (If we didn't restrict the destination IP address, we'd # catch all our outbound queries too.) # # Original Downloaded from: # http://www.hackinglinuxexposed.com/articles/20030730.html # # Modified by huangwei.me 2010-08-02 # 本机IP地址定义 my $MY_IP_ADDRESS='10.2.1.83'; # 非特权权限的uid/gid,可以用id命令查看当前用户的uid和gid my $UNPRIV="1001"; # 自行指定监听网卡,留空将使用系统默认探测到的网卡 my $MY_DEV="vmnet8"; # # No changes required hereafter # use Net::Pcap; use Net::DNS::RR; use FileHandle; use English; # $UID等价于$<, $EUID等价于$> use strict; STDOUT->autoflush(1); while ( 1 ) { my $pid = fork(); if ( ! defined $pid ) { die "Unable to fork. Yikes." }; if ( $pid ) { # Parent process (running as root) will wait for # child. If child exits, we'll create another one. wait(); sleep(1); # To keep us from respawning too fast if necessary. } else { print "Child starting\n"; # Child process will do actual sniffing. # First, create our packet capturing device my($pcap_t) = create_pcap(); unless ( $pcap_t ) { die "Unable to create pcap"; } # Let's stop running as root. Since we already # have our pcap descriptor, we can still use it. $EGID="$UNPRIV $UNPRIV"; # setgid and setgroups() $GID=$UNPRIV; $UID=$UNPRIV; $EUID=$UNPRIV; # Capture packets forever. Net::Pcap::loop($pcap_t, -1, \&process_pkt, 0); # Technically, we shouldn't get here since the loop # is infinite (-1), but just in case, close and exit. Net::Pcap::close($pcap_t); exit 1; } } sub create_pcap { my $promisc = 1; # enter promiscuous mode or not. my $snaplen = 1500; # the maximum number of bytes to capture from each packet my $to_ms = 0; # timeout my $opt=1; # Sure, optimisation is good... my($err,$net,$mask,$dev,$filter_t); my $filter = "udp dst port 53 or udp src port 53"; # pcap capture filter # Look up an appropriate device (eth0 usually) $dev = $MY_DEV || Net::Pcap::lookupdev(\$err); print "sniffing on ", $dev, "\n"; $dev or die "Net::Pcap::lookupdev failed. Error was $err"; if ( (Net::Pcap::lookupnet($dev, \$net, \$mask, \$err) ) == -1 ) { die "Net::Pcap::lookupnet failed. Error was $err"; } # Actually open up our descriptor my $pcap_t = Net::Pcap::open_live($dev, $snaplen, $promisc, $to_ms, \$err); $pcap_t || die "Can't create packet descriptor. Error was $err"; if ( Net::Pcap::compile($pcap_t, \$filter_t, $filter, $opt, $net) == -1 ) { die "Unable to compile filter string '$filter'\n"; } # Make sure our sniffer only captures those bytes we want in # our filter. Net::Pcap::setfilter($pcap_t, $filter_t); # Return our pcap descriptor $pcap_t; } # Routine to process the packet -- called by Net::Pcap::loop() # every time an appropriate packet is snagged. sub process_pkt { my($user_data, $hdr, $pkt) = @_; my($src_ip) = 26; # start of the source IP in the packet my($dst_ip) = 30; # start of the dest IP in the packet my($udp) = 42; # start of UDP pkt payload my($domain_start) = 55; # start of the domain in the packet my($data); # extract the source IP addr into dotted quad form. my($source) = sprintf("%d.%d.%d.%d", ord( substr($pkt, $src_ip, 1) ), ord( substr($pkt, $src_ip+1, 1) ), ord( substr($pkt, $src_ip+2, 1) ), ord( substr($pkt, $src_ip+3, 1) )); # extract the destination IP addr into dotted quad form. my($destination) = sprintf("%d.%d.%d.%d", ord( substr($pkt, $dst_ip, 1) ), ord( substr($pkt, $dst_ip+1, 1) ), ord( substr($pkt, $dst_ip+2, 1) ), ord( substr($pkt, $dst_ip+3, 1) )); $data = substr($pkt, $domain_start); $data =~ s/0.*//g; # strip off everything after the domain $data =~ s/[^-a-zA-Z0-9]/./g; # change the domain component separators # back int to dots. print "$source -> $destination: $data\n" if ( $source and $destination and $data); dump_dns(parse_dns($pkt, $udp)); } # dump dns query & response in details sub dump_dns { my $dns = $_; $dns->print; } sub parse_dns { my($pkt, $udp) = @_; my $udp_payload = substr($pkt, $udp); my $dns = Net::DNS::Packet->new(\$udp_payload); return $dns; }
