Last active
May 27, 2021 22:14
-
-
Save mdeweerd/47ea977028259ee9a6b6fd8f2356c65d to your computer and use it in GitHub Desktop.
Perl script to convert a regular log file as syslog messages in a pcap file for wireshark
This file contains hidden or 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
#!/bin/perl | |
# SCRIPT TO CONVERT TEXT LOG TO PCAP FILE | |
# READS DATES FROM LOG FILE | |
# WHEN READING FROM PIPE (UNTESTED) WITHOUT DATES, | |
# THE CURRENT TIMESTAMP IS USED. | |
# | |
# Potential parameters to the script: | |
# Parameter ending in ".log" is the log input file | |
# Parameter ending in ".pcap" is the log output file | |
# Digit from 0 to 7 is the log index. | |
# Other number is timeshift to apply in seconds (can be floating point 213.213) | |
# | |
# | |
# Requires some packages, you may need to install File::PCAM | |
# | |
# If you have CPAN as a command: `cpan install File::PCAP` | |
# With perl command, and CPAN module: `perl -MCPAN install File::PCAP` | |
# Otherwise: visit the CPAN site, download the package and follow instructions. | |
use File::PCAP::Writer; # Needed to write the pcap file | |
use Time::HiRes qw(gettimeofday); # Needed to get usec precision for current time | |
use Time::Piece; # May no longer be needed, possibly needed for date in Syslog message (disabled) | |
use Time::Local; # needed for gmtime | |
use Date::Parse; # May no longer be needed, possibly needed for date in Syslog message (disabled) | |
use Socket; # Needed for inet_aton, etc - could be avoided | |
use Scalar::Util qw(looks_like_number); # Need to test if argument is a number | |
use List::Util qw(sum); # Needed for the checksum which is not currently used | |
use POSIX qw(locale_h); # Needed for locale type | |
use locale; # Needed for setlocale | |
# Make sure text is in English. | |
setlocale(LC_CTYPE); | |
my $fname; # Default output file name | |
my $logname; # Default log file, when '', use STDIN | |
my $timeshiftSec; # Timeshift to apply to log. | |
my $logidx=0; | |
# Check CLI parameters, parameter selection based on tyep | |
for(my $i = 0; $i < @ARGV; $i++){ | |
if( $ARGV[$i] =~ /^([0-7])$/ ) { | |
$logidx=scalar($1); | |
} elsif( $ARGV[$i] =~ /.pcap$/ ) { | |
if(!defined($fname)) { | |
$fname=$ARGV[$i]; | |
} | |
} elsif(looks_like_number($ARGV[$i])) { | |
if(!defined($timeshiftSec)) { | |
$timeshiftSec=$ARGV[$i]; | |
} | |
} elsif(!defined($logname)) { | |
$logname=$ARGV[$i]; | |
} | |
} | |
# Set defaults for missing options. | |
if(!defined($logname)) { | |
$logname=''; | |
} | |
if(!defined($fname)) { | |
if($logname=~/^(.*).log/) { | |
$fname=$1.'_log.pcap'; | |
} else { | |
$fname='file.pcap'; | |
} | |
} | |
if(!defined($timeshiftSec)) { | |
$timeshiftSec=0; | |
} | |
my $logtype=134+$logidx*8; | |
# Delete the output file | |
unlink($fname); | |
# Open the output pcap file | |
my $fpw = File::PCAP::Writer->new( { | |
fname => $fname, | |
dlt => 1, | |
} ); | |
# Open the input file | |
my $fh; | |
my $timeFromFile=0; | |
if($logname ne '') { | |
open( $fh => $logname) || die "Cannot open $logname: $!"; | |
$timeFromFile=1; | |
} else { | |
$fh=*STDIN; | |
$logname="STDIN"; | |
} | |
# Inform about what we are doing | |
print "Convert $logname to $fname"; | |
# Do conversion | |
my $t=0; | |
my $usec; | |
my $tOffset=int($timeshiftSec); | |
my $usOffset=int(($timeshiftSec-$tOffset)*1000000); | |
if($usOffset<0) { | |
$usOffset+=1000000; | |
$tOffset-=1; | |
} | |
while( my $line = <$fh> ) { | |
chomp; | |
if(length($line)==0||$line=~/^\s*$/) { | |
next; | |
} | |
#print $t; | |
$line=stripAnsi($line); | |
if($line=~/^\[([^[]*?)\]\s?(.*)/) { | |
my $timestr=$1; | |
$line=$2; | |
$t=str2time($1,"+0000"); | |
#print $timestr." ".$t; | |
$usec = 0; | |
$timeFromFile=1; | |
} | |
# Special case where another timestamp is present. | |
if($line=~/^(\[(\d\d):(\d\d):(\d\d)\.(\d\d\d),(\d\d\d)])\s?(.*)/) { | |
my $nhour=$2; | |
my $nmin=$3; | |
my $nsec=$4; | |
$usec=$5*1000; | |
$usec+=$6; | |
$line=$7; | |
#print $1.$usec."\n"; # Original time for debugging | |
my($sec,$min,$heure,$day,$mon,$year) = gmtime($t); | |
$t=timegm($sec,$min,$heure,$day,$mon,$year); | |
$t+=$nmsec/1000; | |
#print $timestr." ".$t."\n"; # For debugging | |
$timeFromFile=1; | |
} | |
if(!$timeFromFile) { | |
($t, $usec) = gettimeofday(); | |
} | |
$line="<${logtype}>".$line; | |
#$line=gmtime($t)->strftime("%M %b %H:%M:%S")." ".$line; | |
#print $line."\n"; | |
#print $usec." "; | |
$buf=getPacketForText($line); | |
$blen=$plen=length($buf); | |
# Adjust times | |
my $nTime=$t+$tOffset; | |
my $uTime=$usec+$usOffset; | |
if($uTime<0||$uTime>=1000000) { | |
my $mTime=$uTime%1000000; | |
$nTime+=($uTime-$mTime)/1000000; | |
$uTime=$mTime; | |
} | |
$fpw->packet( $nTime, $uTime, $blen, $plen, $buf ); | |
} | |
#$fpw->close(); | |
sub stripAnsi { | |
my $l=shift; | |
$l=~s/\e\[[0-9;]*m(?:\e\[K)?//g; # Adam Kats froma https://superuser.com/questions/380772/removing-ansi-color-codes-from-text-stream | |
return $l; | |
} | |
# Helper functions, adapted from https://stackoverflow.com/questions/51061864/sending-spoofed-syslog-messages-in-perl | |
sub udp_checksum { | |
# thanks to ikegami in post https://stackoverflow.com/questions/46181281/udp-checksum-function-in-perl | |
my $packed_src_addr = shift; # As 4-char string, e.g. as returned by inet_aton. | |
my $packed_dst_addr = shift; # As 4-char string, e.g. as returned by inet_aton. | |
my $udp_packet = shift; # UDP header and data as a string. | |
my $sum = sum( | |
IPPROTO_UDP, | |
length($udp_packet), | |
map({ unpack('n*', $_) } | |
$packed_src_addr, | |
$packed_dst_addr, | |
$udp_packet."\0", # Extra byte ignored if length($udp_packet) is even. | |
), | |
); | |
while (my $hi = $sum >> 16) { | |
$sum = ($sum & 0xFFFF) + $hi; | |
} | |
return ~$sum & 0xFFFF; | |
} | |
sub getPacketForText { | |
$data=shift; | |
$src_host = "localhost"; | |
$dst_host = 127.0.0.1; | |
$src_port = 33333; | |
$dest_port = 514; | |
$cksum = 0; #initialise, we will sort this later | |
#$udp_len is the udp packet length in the udp header. Its just 8 plus the length of the data | |
#for this test harness we will set the data here too | |
#$data = "<132> %FWSM-3-106010: Deny inbound tcp src outside:215.251.218.222/11839 dst inside:192.168.1.1/369"; | |
#$data="<132> May 22 08:59:12 pf: 00:00:35.159056 rule 3/0(match): block in on em2: (tos 0x0, ttl 128, id 6204, offset 0, flags [none], proto UDP (17), length 235)"; | |
$udp_len = 8+length($data); | |
$udp_proto = 17; #17 is the code for udp | |
#Prepare the udp packet, needed for the checksum to happen, then get the checksum (horrifically complicated, just google it) | |
$udp_packet = pack("nnnna*", $src_port,$dest_port,$udp_len, $cksum, $data); | |
$cksum = udp_checksum(inet_aton($src_host),inet_aton($dst_host),$udp_packet); | |
$zero_cksum = 0; | |
#test harness checks about host IPs | |
my $dst_host = (gethostbyname($dst_host))[4]; my $src_host = (gethostbyname($src_host))[4]; | |
# Now lets construct the IP packet | |
my $ip_ver = 4; | |
my $ip_len = 5; | |
my $ip_ver_len = $ip_ver . $ip_len; | |
my $ip_tos = 00; | |
my ($ip_tot_len) = $udp_len + 20; | |
my $ip_frag_id = 19245; | |
my $ip_frag_flag = "010"; | |
my $ip_frag_oset = "0000000000000"; | |
my $ip_fl_fr = $ip_frag_flag . $ip_frag_oset; | |
my $ip_ttl = 30; | |
#H2H2nnB16C2na4a4 for the IP Header part#nnnna* for the UDP Header part. | |
#To understand these, see the manual of pack function and IP and UDP Header formats | |
#IP checksum ($zero_cksum is calculated by the kernel. Dont worry about it.) | |
my ($pkt) = pack("H*","0000000000000000000000000800") | |
#.pack('H2H2nnB16C2na4a4nnnna*', | |
.pack('H2H2nnB16C2na4a4nnnna*', | |
$ip_ver_len,$ip_tos,$ip_tot_len,$ip_frag_id, | |
$ip_fl_fr,$ip_ttl,$udp_proto,$zero_cksum,$src_host, | |
$dst_host,$src_port,$dest_port, | |
$udp_len, $cksum, $data); | |
##bit that makes the socket and sends the packet | |
##socket(RAW, AF_INET, SOCK_RAW, 255) || die $!; setsockopt(RAW, 0, 1, 1); | |
##my ($destination) = pack('Sna4x8', AF_INET, $dest_port, $dst_host); | |
##send(RAW,$pkt,0,$destination); | |
#replace by return | |
return $pkt; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment