なんだか、数年毎に再アップしてる気がしますが。まあいいや。
このscriptをtcpfilterとか適当な名前で保存しておいて、たとえば
# tcpdump -xnls 0 port 80 | ./tcpfilter
とかすると、tcpdumpのhexなパケットを整形してくれます。
SIPとかで使うと超便利です。
CPANも何も使ってないので、ただperlさえ入ってれば動くのがいいとこかな。
このscriptさえコピっていけばいいので。
remove_control_codeの中の正規表現をコメントアウトすると、制御コードも目視できますわ。
#!/usr/bin/perl
#
# tcpfilter - analyze and format an output of tcpdump
#
# - zaki - <zaki@redsip.com>
# $Id: tcpfilter,v 1.5 2004/06/02 06:14:51 zaki Exp zaki $
#
use strict;
use warnings;
use English;
$OUTPUT_AUTOFLUSH = 1;
sub remove_sp {
my($data) = @_;
$data =~ s|\s||g;
return $data;
}
sub get_total_len {
my($data) = @_;
my @data = split(m||, $data);
my $total_len = hex("$data[5 - 1]$data[6 - 1]$data[7 - 1]$data[8 - 1]") * 2;
return $total_len;
}
sub output {
my($data, $time, $count) = @_;
ip_sanity_check($data) or return;
my @data = split(m||, $data);
my $ip_len = ip_header_len(@data);
my $src_ip = ip_src(@data);
my $dst_ip = ip_dst(@data);
my $proto = protocol_type(@data);
my($header_len, $src_port, $dst_port);
if($proto eq '00000110') {
check_syn($ip_len, @data) and return;
my $tcp_len = tcp_header_len($ip_len, @data);
$src_port = tcp_src_port($ip_len, @data);
$dst_port = tcp_dst_port($ip_len, @data);
$header_len = $ip_len + $tcp_len;
} elsif($proto eq '00010001') {
my $udp_len = udp_header_len();
$src_port = udp_src_port($ip_len, @data);
$dst_port = udp_dst_port($ip_len, @data);
$header_len = $ip_len + $udp_len;
} else {
return;
}
$data = truncate_header($header_len, $data);
$data = hex_to_ascii($data);
$data = remove_control_code($data);
print_packet($data, $time, $count, $src_ip, $dst_ip, $src_port, $dst_port);
}
sub ip_sanity_check {
my($data) = @_;
return $data =~ m|^4[0-9A-Fa-f]{3}| ? 1 : 0;
}
sub ip_header_len {
my @data = @_;
my $ip_len = hex($data[2 - 1]) * 2 * 4;
return $ip_len;
}
sub ip_src {
my @data = @_;
my $src_ip = hex("$data[25 - 1]$data[26 - 1]") . '.' .
hex("$data[27 - 1]$data[28 - 1]") . '.' .
hex("$data[29 - 1]$data[30 - 1]") . '.' .
hex("$data[31 - 1]$data[32 - 1]");
return $src_ip;
}
sub ip_dst {
my @data = @_;
my $dst_ip = hex("$data[33 - 1]$data[34 - 1]") . '.' .
hex("$data[35 - 1]$data[36 - 1]") . '.' .
hex("$data[37 - 1]$data[38 - 1]") . '.' .
hex("$data[39 - 1]$data[40 - 1]");
return $dst_ip;
}
sub protocol_type {
my @data = @_;
my $proto = unpack("B8", pack("H2", "$data[19 - 1]$data[20 - 1]"));
return $proto;
}
sub check_syn {
my($ip_len, @data) = @_;
my $tcp_flag = unpack("B4", pack("H1", $data[$ip_len + 28 - 1]));
my @tcp_flag = split(m||, $tcp_flag);
return $tcp_flag[2] eq '1' ? 1 : 0;
}
sub tcp_header_len {
my($ip_len, @data) = @_;
my $tcp_offset = $ip_len + 25;
my $tcp_len = hex($data[$tcp_offset - 1]) * 2 * 4;
return $tcp_len;
}
sub tcp_src_port {
my($ip_len, @data) = @_;
my $port_hex = $data[$ip_len + 1 - 1] . $data[$ip_len + 2 - 1] .
$data[$ip_len + 3 - 1] . $data[$ip_len + 4 - 1];
my $src_port = hex($port_hex);
return $src_port;
}
sub tcp_dst_port {
my($ip_len, @data) = @_;
my $port_hex = $data[$ip_len + 5 - 1] . $data[$ip_len + 6 - 1] .
$data[$ip_len + 7 - 1] . $data[$ip_len + 8 - 1];
my $dst_port = hex($port_hex);
return $dst_port;
}
sub udp_header_len {
return 16;
}
sub udp_src_port {
my($ip_len, @data) = @_;
my $port_hex = $data[$ip_len + 1 - 1] . $data[$ip_len + 2 - 1] .
$data[$ip_len + 3 - 1] . $data[$ip_len + 4 - 1];
my $src_port = hex($port_hex);
return $src_port;
}
sub udp_dst_port {
my($ip_len, @data) = @_;
my $port_hex = $data[$ip_len + 5 - 1] . $data[$ip_len + 6 - 1] .
$data[$ip_len + 7 - 1] . $data[$ip_len + 8 - 1];
my $dst_port = hex($port_hex);
return $dst_port;
}
sub truncate_header {
my($header_len, $data) = @_;
$data =~ s|^.{$header_len}||;
return $data;
}
sub hex_to_ascii {
my($data) = @_;
$data =~ s|[0-9A-Fa-f][0-9A-Fa-f]|pack("C1", hex($MATCH))|ge;
return $data;
}
sub remove_control_code {
my($data) = @_;
$data =~ s|[\x00-\x07\x0e-\x1f\x80-\xff]||g;
return $data;
}
sub print_packet {
my($data, $time, $count, $src_ip, $dst_ip, $src_port, $dst_port) = @_;
length $data > 0 or return;
print "===== $count $time $src_ip:$src_port $dst_ip:$dst_port =====\r\n";
print $data, "\r\n";
}
my $time;
my $buf = '';
my $len = 0;
my $padding = 0;
my $count = 0;
while(my $line = <STDIN>) {
if($line =~ m|^\s+(?:0x[0-9a-f]{4}:\s+)?((?:[0-9a-f]{1,4} ?)+)(\s{2})?|) {
$padding and next;
$buf .= remove_sp($1);
if($len > 0) {
if(length $buf >= $len) {
output($buf, $time, $count);
$padding++;
}
} else {
$len = get_total_len($buf);
}
} elsif($line =~ m|^(\d{2}:\d{2}:\d{2}\.\d+)|) {
$count++;
$time = $1;
$buf = '';
$len = 0;
$padding = 0;
}
}