add zephyr's excellent pcat

This commit is contained in:
pi-rho 2013-02-05 13:02:02 -06:00
parent 249e959fcc
commit 718137b450
4 changed files with 500 additions and 0 deletions

107
docs/pcat.mdoc Normal file
View File

@ -0,0 +1,107 @@
.\" This manual is Copyright 2012 by pi-rho <ubuntu@tyr.cx>
.\"
.\" This program is free software: you can redistribute it and/or modify
.\" it under the terms of the GNU General Public License as published by
.\" the Free Software Foundation, either version 3 of the License, or
.\" (at your option) any later version.
.\"
.\" This package is distributed in the hope that it will be useful,
.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
.\" GNU General Public License for more details.
.\"
.\" You should have received a copy of the GNU General Public License
.\" along with this program. If not, see <http://www.gnu.org/licenses/>.
.\"
.\" On Debian systems, the complete text of the GNU General
.\" Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".
.
.Dd May 23, 2012
.Dt PCAT 1
.Os "Network Reverse Engineering Toolkit" 1.1337
.
.Sh NAME
.Nm pcat
.Nd dump a packet capture in a line-based, parsable format
.
.Sh SYNOPSIS
.Nm pcat
.Op Fl h | Fl v
.Nm pcat
.Op Fl o Ar output.txt
.Op Pa input.pcap
.
.Sh DESCRIPTION
The basic concept for this utility is to dump the contents of a packet capture
in a line-based, parsable format. The general format is as follows:
.Pp
.D1 Sy TIMESTAMP PROTOCOL SOURCE DESTINATION PAYLOAD
.Pp
Each value may have attributes separated by commas.
.Pp
.Bl -tag -width "destination"
.It Sy TIMESTAMP
epoch,nanoseconds
.It Sy PROTOCOL
ARP, TCP4, UDP4, ICMP4, P# (where # is the IP protocol number)
.It Sy SOURCE
.Em ARP No \ : macaddress,ipaddress
.br
.Em ICMP4 : No ipaddress,type,code
.br
.Em TCP4 No : ipaddress,port,sequence
.br
.Em UDP4 No : ipaddress,port
.br
.Em P# No \ \ : ipaddress
.It Sy DESTINATION
.Em ARP No \ : macaddress,ipaddress
.br
.Em ICMP4 : No ipaddress
.br
.Em TCP4 No : ipaddress,port,ack
.br
.Em UDP4 No : ipaddress,port
.br
.Em P# No \ \ : ipaddress
.It Sy PAYLOAD
a hexadecimal representation of the protocol's payload, or '-' if there is no
payload
.El
.Pp
The available options include:
.Pp
.Bl -tag -compact -width "-o output.txt"
.It Fl h
usage information
.It Fl v
the program's version
.It Fl o Ar output.txt
sets the output filename to
.Pa output.txt
.Pq default: Dv stdout
.It Ar input.pcap
the packet capture to read
.Pq default: Dv stdin
.El
.
.Sh EXAMPLES
As a filter:
.D1 Ic $ Nm pmerge Pa one.pcap Pa two.pcap | Nm pcat > Pa output.txt
.Pp
In simple command form:
.D1 Ic $ Nm pcat Fl o Pa output.txt Pa one.pcap
.
.Sh SEE ALSO
.Xr p4split 1 ,
.Xr puniq 1 ,
.Xr pmerge 1
.
.Sh AUTHORS
.An Zephyr Aq Ad zephyr@dirtbags.net ,
.An pi-rho Aq Ad pi-rho@tyr.cx
.
.Sh BUGS
Bugs may be submitted at
.Aq Ad https://bugs.launchpad.net/netre-tools
.\" vim:ft=mandoc

306
src/pcat.c Normal file
View File

@ -0,0 +1,306 @@
/* ____ __ ____ ______
* | \ / ] / T| T
* | o )/ / Y o || |
* | _// / | |l_j l_j
* | | / \_ | _ | | |
* | | \ || | | | |
* l__j \____jl__j__j l__j
*
* dump a packet capture in a line-based, parsable format
*
*/
#include <getopt.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <sysexits.h>
#include "netre.h"
#include "pcap.h"
#include "stream.h"
#define IPPROTO_ICMP 1
#define IPPROTO_TCP 6
#define IPPROTO_UDP 17
#define ARP_REQ 0x01
#define ARP_REP 0x02
#define TH_FIN 0x01
#define TH_SYN 0x02
#define TH_RST 0x04
#define TH_PSH 0x08
#define TH_ACK 0x10
#define TH_URG 0x20
int version(bool error) {
fprintf(WHICHOUT(error), "pcat v.%s - %s\n\n", PACKAGE_VERSION,
"dump a packet capture in a line-based, parsable format");
return error ? EX_USAGE : EX_OK;
}
int usage(bool error, char *prog) {
int retval = version(error);
fprintf(WHICHOUT(error), "Usage: %s [-o OUTPUT] [FILE]\n", prog);
fprintf(WHICHOUT(error), "\t-o OUTPUT file in which to write output; if not specified,\n");
fprintf(WHICHOUT(error), "\t output will be written to stdout\n");
fprintf(WHICHOUT(error), "\tFILE input pcap file\n");
return retval;
}
void mac_addr(char *addr_s, uint16_t addr1, uint16_t addr2, uint16_t addr3) {
snprintf(addr_s, 18, "%02x:%02x:%02x:%02x:%02x:%02x",
(addr1 >> 8) & 0xff,
(addr1 >> 0) & 0xff,
(addr2 >> 8) & 0xff,
(addr2 >> 0) & 0xff,
(addr3 >> 8) & 0xff,
(addr3 >> 0) & 0xff);
}
void ip4_addr(char *addr_s, uint32_t addr) {
snprintf(addr_s, 16, "%u.%u.%u.%u",
(addr >> 24) & 0xff,
(addr >> 16) & 0xff,
(addr >> 8) & 0xff,
(addr >> 0) & 0xff);
}
void print_payload(struct stream *s, FILE *out) {
if (s->len == 0)
fprintf(out, "-");
else
while (s->len)
fprintf(out, "%02x", read_uint8(s));
}
void process_icmp(struct stream *s, char *saddr_s, char *daddr_s, FILE *out) {
uint8_t type = read_uint8(s);
uint8_t code = read_uint8(s);
uint16_t chksum = read_uint16be(s);
fprintf(out, "ICMP4 %s,%u,%u %s ", saddr_s, type, code, daddr_s);
if (false && chksum && false) {
/* Placate compiler */
}
}
void process_tcp(struct stream *s, char *saddr_s, char *daddr_s, FILE *out) {
uint16_t sport = read_uint16be(s);
uint16_t dport = read_uint16be(s);
uint32_t seq = read_uint32be(s);
uint32_t ack = read_uint32be(s);
uint8_t off = read_uint8(s);
uint8_t hlen = (off >> 4) * 4;
uint8_t flags = read_uint8(s);
uint16_t window = read_uint16be(s);
uint16_t chksum = read_uint16be(s);
uint16_t urgent = read_uint16be(s);
if (hlen < 20) printf("!");
fprintf(out, "TCP4 %s,%u,%u %s,%u,%u ", saddr_s, sport, seq, daddr_s, dport, ack);
if (false && urgent && chksum && window && flags && ack && seq && false) {
/* Placate compiler */
}
}
void process_udp(struct stream *s, char *saddr_s, char *daddr_s, FILE *out) {
uint16_t sport = read_uint16be(s);
uint16_t dport = read_uint16be(s);
uint16_t len = read_uint16be(s);
uint16_t chksum = read_uint16be(s);
fprintf(out, "UDP4 %s,%u %s,%u ", saddr_s, sport, daddr_s, dport);
if (false && len && chksum && false) {
/* Placate compiler */
}
}
void process_arp(struct stream *s, FILE *out) {
uint16_t htype = read_uint16be(s);
uint16_t ptype = read_uint16be(s);
uint8_t hlen = read_uint8(s);
uint8_t plen = read_uint8(s);
uint16_t op = read_uint16be(s);
uint16_t sha1 = read_uint16be(s);
uint16_t sha2 = read_uint16be(s);
uint16_t sha3 = read_uint16be(s);
uint32_t spa = read_uint32be(s);
uint16_t tha1 = read_uint16be(s);
uint16_t tha2 = read_uint16be(s);
uint16_t tha3 = read_uint16be(s);
uint32_t tpa = read_uint32be(s);
// 12:34:56:78:9A:BC
char sha_s[20];
char spa_s[20];
char tha_s[20];
char tpa_s[20];
mac_addr(sha_s, sha1, sha2, sha3);
ip4_addr(spa_s, spa);
mac_addr(tha_s, tha1, tha2, tha3);
ip4_addr(tpa_s, tpa);
switch(op) {
case ARP_REQ:
fprintf(out, "ARP? "); break;
case ARP_REP:
fprintf(out, "ARP. "); break;
default:
fprintf(out, "ARP! "); break;
}
fprintf(out, "%s,%s %s,%s -", sha_s, spa_s, tha_s, tpa_s);
if (false && htype && ptype && hlen && plen && false) {
/* Placate compiler */
}
}
void process_ip4(struct stream *s, FILE *out) {
uint8_t vhl = read_uint8(s);
uint8_t ihl = (vhl & 0x0f) * 4;
if (ihl < 20) ihl = 20;
uint8_t tos = read_uint8(s);
uint16_t length = read_uint16be(s);
uint16_t id = read_uint16be(s);
uint16_t off = read_uint16be(s);
uint8_t ttl = read_uint8(s);
uint8_t proto = read_uint8(s);
uint16_t chksum = read_uint16be(s);
uint32_t saddr = read_uint32be(s);
uint32_t daddr = read_uint32be(s);
char saddr_s[20];
char daddr_s[20];
ip4_addr(saddr_s, saddr);
ip4_addr(daddr_s, daddr);
DDUMP_d(ihl);
DDUMP_d(length);
// Ignore options
sskip(s, 20 - ihl);
// Force stream length to IP payload length
s->len = length - ihl;
DDUMP_d(proto);
switch (proto) {
case IPPROTO_ICMP:
process_icmp(s, saddr_s, daddr_s, out); break;
case IPPROTO_TCP:
process_tcp( s, saddr_s, daddr_s, out); break;
case IPPROTO_UDP:
process_udp( s, saddr_s, daddr_s, out); break;
default:
fprintf(out, "P%d %s %s ", proto, saddr_s, daddr_s); break;
}
print_payload(s, out);
if (false && chksum && id && tos && ttl && off && false) {
/* Placate compiler */
}
}
void print_ethernet(struct stream *s, FILE *out) {
uint8_t saddr[6];
uint8_t daddr[6];
uint16_t vlan;
uint16_t ethertype;
sread(s, &saddr, sizeof(saddr));
sread(s, &daddr, sizeof(daddr));
ethertype = read_uint16be(s);
if (ethertype == 0x8100) { // VLAN
vlan = read_uint16be(s);
ethertype = read_uint16be(s);
}
switch (ethertype) {
case 0x0806: // ARP
process_arp(s, out); break;
case 0x0800: // IPv4
default:
DDUMP_x(ethertype);
process_ip4(s, out); break;
}
if (false && vlan && false) {
/* Placate compiler */
}
}
void print_frame(struct pcap_pkthdr *hdr, char const *frame, int raw, FILE *out) {
struct stream streambuf;
struct stream *s = &streambuf;
sinit(s, frame, hdr->caplen);
fprintf(out, "%u.%6.6u ", hdr->ts.tv_sec, hdr->ts.tv_usec);
if (raw)
process_ip4(s, out);
else
print_ethernet(s, out);
fprintf(out, "\n");
}
int pcat(FILE *f, FILE *out) {
struct pcap_file p;
if (pcap_open_in(&p, f) == -1) {
return EX_IOERR;
}
for (;;) {
struct pcap_pkthdr hdr;
char frame[MAXFRAME];
if (pcap_read_pkthdr(&p, &hdr) == -1) break;
if (fread(frame, hdr.caplen, 1, f) != 1) break;
print_frame(&hdr, frame, p.raw, out);
}
return EX_OK;
}
int main(int argc, char *argv[]) {
int opt;
FILE *output = stdout;
FILE *input = stdin;
while ((opt = getopt(argc, argv, "hvo:")) != -1) {
switch (opt) {
case 'o': /* output file */
output = fopen(optarg, "wb");
if (!output) {
perror("opening output");
return EX_OSFILE;
}
break;
case 'v': return version(false);
case 'h': return usage(false, argv[0]);
default: return usage(true, argv[0]);
}
}
if (optind < argc) {
input = fopen(argv[optind], "rb");
if (!input) {
perror("opening input");
return EX_OSFILE;
}
}
return pcat(input, output);
}

65
src/stream.c Normal file
View File

@ -0,0 +1,65 @@
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include "stream.h"
void sinit(struct stream *s, char const *buf, size_t buflen) {
s->buf = buf;
s->len = buflen;
}
bool sskip(struct stream *s, size_t count) {
if (count > s->len) {
s->len = 0;
return false;
}
s->buf += count;
s->len -= count;
return true;
}
bool sread(struct stream *s, void *buf, size_t count) {
void const *d = s->buf;
if (!sskip(s, count)) return false;
memcpy(buf, d, count);
return true;
}
uint8_t read_uint8(struct stream *s) {
uint8_t *d = (uint8_t *)s->buf;
if (!sskip(s, 1)) return 0;
return d[0];
}
uint16_t read_uint16be(struct stream *s) {
uint8_t *d = (uint8_t *)s->buf;
if (!sskip(s, 2)) return 0;
return ((d[0] << 8) | (d[1] << 0));
}
uint16_t read_uint16le(struct stream *s) {
uint8_t *d = (uint8_t *)s->buf;
if (!sskip(s, 2)) return 0;
return ((d[0] << 0) | (d[1] << 8));
}
uint32_t read_uint32be(struct stream *s) {
uint8_t *d = (uint8_t *)s->buf;
if (!sskip(s, 4)) return 0;
return ((d[0] << 24) | (d[1] << 16) | (d[2] << 8) | (d[3] << 0));
}
uint32_t read_uint32le(struct stream *s) {
uint8_t *d = (uint8_t *)s->buf;
if (!sskip(s, 4)) return 0;
return ((d[0] << 0) | (d[1] << 8) | (d[2] << 16) | (d[3] << 24));
}

22
src/stream.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef _STREAM_H
#define _STREAM_H
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
struct stream {
char const *buf;
size_t len;
};
void sinit(struct stream *s, char const *buf, size_t len);
bool sskip(struct stream *s, size_t count);
bool sread(struct stream *s, void *buf, size_t count);
uint8_t read_uint8(struct stream *s);
uint16_t read_uint16be(struct stream *s);
uint16_t read_uint16le(struct stream *s);
uint32_t read_uint32be(struct stream *s);
uint32_t read_uint32le(struct stream *s);
#endif