mirror of
https://github.com/radareorg/radare2
synced 2026-08-22 20:23:32 -04:00
Fix #25480 - pcap stream selection support ##bin
This commit is contained in:
parent
9f345aaaf8
commit
b4e48fb498
5 changed files with 533 additions and 203 deletions
|
|
@ -6,6 +6,7 @@ void pcap_obj_free(pcap_obj_t *obj) {
|
|||
if (obj) {
|
||||
free (obj->header);
|
||||
r_list_free (obj->recs);
|
||||
r_list_free (obj->streams);
|
||||
r_unref (obj->b);
|
||||
free (obj);
|
||||
}
|
||||
|
|
@ -29,62 +30,62 @@ void pcaprec_free(pcaprec_t *rec) {
|
|||
free (rec->hdr);
|
||||
free (rec->link.ether_hdr);
|
||||
free (rec->net.ipv4_hdr);
|
||||
free (rec->transport.tcp_hdr);
|
||||
free (rec->data);
|
||||
free (rec);
|
||||
}
|
||||
}
|
||||
|
||||
static bool parse_tcp(RBuffer *b, ut64 off, pcaprec_t *rec, ut32 size) {
|
||||
pcaprec_tcp_t *tcp = R_NEW0 (pcaprec_tcp_t);
|
||||
ut8 buf[sizeof (pcaprec_tcp_t)] = {0};
|
||||
r_buf_read_at (b, off, buf, sizeof (buf));
|
||||
tcp->src_port = r_read_at_be16 (buf, 0);
|
||||
tcp->dst_port = r_read_at_be16 (buf, 2);
|
||||
tcp->seq_num = r_read_at_be32 (buf, 4);
|
||||
tcp->ack_num = r_read_at_be32 (buf, 8);
|
||||
tcp->hdr_len = r_read_at_be8 (buf, 12);
|
||||
tcp->flags = r_read_at_be16 (buf, 13);
|
||||
tcp->win_sz = r_read_at_be16 (buf, 15);
|
||||
tcp->chksum = r_read_at_be16 (buf, 17);
|
||||
tcp->urgnt_ptr = r_read_at_be16 (buf, 19);
|
||||
// end of the captured bytes of this record
|
||||
static ut64 rec_end(pcaprec_t *rec) {
|
||||
return rec->paddr + sizeof (pcaprec_hdr_t) + rec->hdr->incl_len;
|
||||
}
|
||||
|
||||
// data offset at (((tcp->hdr_len >> 4) & 0x0F) * 4)
|
||||
ut32 dataoff = ((tcp->hdr_len & 0xF0) >> 2);
|
||||
if (dataoff > size) {
|
||||
free (tcp);
|
||||
return false;
|
||||
static void parse_tcp(RBuffer *b, ut64 off, pcaprec_t *rec, ut32 size) {
|
||||
ut8 buf[20];
|
||||
if (size < sizeof (buf) || r_buf_read_at (b, off, buf, sizeof (buf)) != sizeof (buf)) {
|
||||
return;
|
||||
}
|
||||
rec->datasz = size - dataoff;
|
||||
#if 0
|
||||
rec->data = malloc (rec->datasz + 1);
|
||||
if (!rec->data) {
|
||||
free (tcp);
|
||||
return false;
|
||||
ut32 hdrlen = (buf[12] >> 4) * 4;
|
||||
if (hdrlen < sizeof (buf) || hdrlen > size) {
|
||||
return;
|
||||
}
|
||||
r_buf_read_at (b, off + dataoff, rec->data, rec->datasz);
|
||||
if (r_str_nlen ((const char *)rec->data, rec->datasz) > 10) {
|
||||
#if 0
|
||||
eprintf ("0x%08x ", off + dataoff);
|
||||
fflush (stderr);
|
||||
write (1, rec->data, rec->datasz);
|
||||
write (1, "\n", 1);
|
||||
rec->data[10] = 0;
|
||||
#endif
|
||||
// eprintf ("SIZE %d %d : %s\n", off + dataoff, rec->datasz, rec->data);
|
||||
rec->data[rec->datasz] = 0;
|
||||
rec->transport.tcp_hdr = tcp;
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
rec->transport.tcp_hdr = tcp;
|
||||
return true;
|
||||
rec->proto = TRANSPORT_TCP;
|
||||
rec->sport = r_read_at_be16 (buf, 0);
|
||||
rec->dport = r_read_at_be16 (buf, 2);
|
||||
rec->seq = r_read_at_be32 (buf, 4);
|
||||
rec->dataoff = off + hdrlen;
|
||||
rec->datasz = size - hdrlen;
|
||||
}
|
||||
|
||||
#endif
|
||||
free (rec->data);
|
||||
free (rec);
|
||||
return false;
|
||||
// memcpy (rec->data, buf + dataoff, rec->datasz);
|
||||
static void parse_udp(RBuffer *b, ut64 off, pcaprec_t *rec, ut32 size) {
|
||||
ut8 buf[8];
|
||||
if (size < sizeof (buf) || r_buf_read_at (b, off, buf, sizeof (buf)) != sizeof (buf)) {
|
||||
return;
|
||||
}
|
||||
ut32 len = R_MIN (r_read_at_be16 (buf, 4), size);
|
||||
if (len < sizeof (buf)) {
|
||||
return;
|
||||
}
|
||||
rec->proto = TRANSPORT_UDP;
|
||||
rec->sport = r_read_at_be16 (buf, 0);
|
||||
rec->dport = r_read_at_be16 (buf, 2);
|
||||
rec->dataoff = off + sizeof (buf);
|
||||
rec->datasz = len - sizeof (buf);
|
||||
}
|
||||
|
||||
static void parse_transport(RBuffer *b, ut64 off, pcaprec_t *rec, ut8 proto, ut32 size) {
|
||||
ut64 end = rec_end (rec);
|
||||
if (off >= end) {
|
||||
return;
|
||||
}
|
||||
size = R_MIN (size, end - off);
|
||||
switch (proto) {
|
||||
case TRANSPORT_TCP:
|
||||
parse_tcp (b, off, rec, size);
|
||||
break;
|
||||
case TRANSPORT_UDP:
|
||||
parse_udp (b, off, rec, size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static bool parse_ipv4(RBuffer *b, ut64 off, pcaprec_t *rec) {
|
||||
|
|
@ -102,18 +103,9 @@ static bool parse_ipv4(RBuffer *b, ut64 off, pcaprec_t *rec) {
|
|||
ipv4->src = r_read_at_be32 (buf, 12);
|
||||
ipv4->dst = r_read_at_be32 (buf, 16);
|
||||
|
||||
switch (ipv4->protocol) {
|
||||
case TRANSPORT_TCP:
|
||||
{
|
||||
ut32 tcpoff = ((ipv4->ver_len & 0x0F) * 4);
|
||||
if (!parse_tcp (b, off + tcpoff, rec, ipv4->tot_len - tcpoff)) {
|
||||
free (ipv4);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
ut32 hdrlen = (ipv4->ver_len & 0x0F) * 4;
|
||||
if (hdrlen >= sizeof (buf) && ipv4->tot_len >= hdrlen) {
|
||||
parse_transport (b, off + hdrlen, rec, ipv4->protocol, ipv4->tot_len - hdrlen);
|
||||
}
|
||||
rec->net.ipv4_hdr = ipv4;
|
||||
return true;
|
||||
|
|
@ -125,16 +117,12 @@ static bool parse_ipv6(RBuffer *b, ut64 off, pcaprec_t *rec) {
|
|||
r_buf_read_at (b, off, (ut8*)buf, sizeof (pcaprec_ipv6_t));
|
||||
ipv6->vc_flow = r_read_at_be32 (buf, 0);
|
||||
ipv6->plen = r_read_at_be16 (buf, 4);
|
||||
switch (ipv6->nxt) {
|
||||
case TRANSPORT_TCP:
|
||||
if (!parse_tcp (b, off + sizeof (pcaprec_ipv6_t), rec, ipv6->plen)) {
|
||||
free (ipv6);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ipv6->nxt = buf[6];
|
||||
ipv6->hlim = buf[7];
|
||||
memcpy (ipv6->src, buf + 8, 16);
|
||||
memcpy (ipv6->dst, buf + 24, 16);
|
||||
parse_transport (b, off + sizeof (pcaprec_ipv6_t), rec, ipv6->nxt, ipv6->plen);
|
||||
rec->v6 = true;
|
||||
rec->net.ipv6_hdr = ipv6;
|
||||
return true;
|
||||
}
|
||||
|
|
@ -218,6 +206,70 @@ error:
|
|||
return false;
|
||||
}
|
||||
|
||||
static void pcap_stream_free(pcap_stream_t *s) {
|
||||
if (s) {
|
||||
r_list_free (s->recs);
|
||||
free (s);
|
||||
}
|
||||
}
|
||||
|
||||
// endpoint as 16 byte address + 2 byte port, so they can be compared with memcmp
|
||||
static void rec_endpoint(pcaprec_t *rec, bool dst, ut8 *ep) {
|
||||
memset (ep, 0, 16);
|
||||
if (rec->v6) {
|
||||
memcpy (ep, dst? rec->net.ipv6_hdr->dst: rec->net.ipv6_hdr->src, 16);
|
||||
} else {
|
||||
r_write_be32 (ep, dst? rec->net.ipv4_hdr->dst: rec->net.ipv4_hdr->src);
|
||||
}
|
||||
r_write_be16 (ep + 16, dst? rec->dport: rec->sport);
|
||||
}
|
||||
|
||||
static bool pcap_obj_init_streams(pcap_obj_t *obj) {
|
||||
obj->streams = r_list_newf ((RListFree)pcap_stream_free);
|
||||
HtPP *ht = ht_pp_new0 ();
|
||||
if (!obj->streams || !ht) {
|
||||
ht_pp_free (ht);
|
||||
return false;
|
||||
}
|
||||
RListIter *iter;
|
||||
pcaprec_t *rec;
|
||||
r_list_foreach (obj->recs, iter, rec) {
|
||||
if (!rec->proto) {
|
||||
continue;
|
||||
}
|
||||
ut8 ep[2][18];
|
||||
rec_endpoint (rec, false, ep[0]);
|
||||
rec_endpoint (rec, true, ep[1]);
|
||||
// the key orders the endpoints, so both directions map to the same stream
|
||||
int lo = memcmp (ep[0], ep[1], sizeof (ep[0])) > 0;
|
||||
ut8 raw[1 + sizeof (ep)] = { rec->proto };
|
||||
memcpy (raw + 1, ep[lo], sizeof (ep[0]));
|
||||
memcpy (raw + 1 + sizeof (ep[0]), ep[!lo], sizeof (ep[0]));
|
||||
char key[sizeof (raw) * 2 + 1];
|
||||
r_hex_bin2str (raw, sizeof (raw), key);
|
||||
pcap_stream_t *s = ht_pp_find (ht, key, NULL);
|
||||
if (!s) {
|
||||
s = R_NEW0 (pcap_stream_t);
|
||||
s->id = r_list_length (obj->streams);
|
||||
s->proto = rec->proto;
|
||||
s->v6 = rec->v6;
|
||||
memcpy (s->ip[0], ep[0], 16);
|
||||
memcpy (s->ip[1], ep[1], 16);
|
||||
s->port[0] = rec->sport;
|
||||
s->port[1] = rec->dport;
|
||||
s->recs = r_list_new ();
|
||||
r_list_append (obj->streams, s);
|
||||
ht_pp_insert (ht, key, s);
|
||||
}
|
||||
rec->stream = s;
|
||||
rec->dir = memcmp (ep[0], s->ip[0], 16) || rec->sport != s->port[0];
|
||||
s->bytes[rec->dir] += rec->datasz;
|
||||
r_list_append (s->recs, rec);
|
||||
}
|
||||
ht_pp_free (ht);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool pcap_obj_init(pcap_obj_t *obj) {
|
||||
switch (r_buf_read_be32_at (obj->b, 0)) {
|
||||
case PCAP_MAGIC_LE:
|
||||
|
|
@ -239,10 +291,7 @@ static bool pcap_obj_init(pcap_obj_t *obj) {
|
|||
default:
|
||||
return false;
|
||||
}
|
||||
if (pcap_obj_init_hdr (obj) && pcap_obj_init_recs (obj)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return pcap_obj_init_hdr (obj) && pcap_obj_init_recs (obj) && pcap_obj_init_streams (obj);
|
||||
}
|
||||
|
||||
pcap_obj_t *pcap_obj_new_buf(RBuffer *buf) {
|
||||
|
|
@ -264,15 +313,21 @@ void pcaprec_frame_sym_add(RVecRBinSymbol *vec, pcaprec_t *rec, int n) {
|
|||
ptr->paddr = ptr->vaddr = rec->paddr;
|
||||
}
|
||||
|
||||
static void pcaprec_tcp_sym_add(RVecRBinSymbol *vec, pcaprec_t* rec, ut64 paddr, int size) {
|
||||
pcaprec_tcp_t *tcp = rec->transport.tcp_hdr;
|
||||
if (!tcp) {
|
||||
static void pcaprec_transport_sym_add(RVecRBinSymbol *vec, pcaprec_t *rec, ut64 paddr) {
|
||||
const char *name;
|
||||
switch (rec->proto) {
|
||||
case TRANSPORT_TCP:
|
||||
name = "Transmission Control Protocol";
|
||||
break;
|
||||
case TRANSPORT_UDP:
|
||||
name = "User Datagram Protocol";
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
RBinSymbol *ptr = RVecRBinSymbol_emplace_back (vec);
|
||||
int datasz = size - ((tcp->hdr_len & 0xF0) >> 2);
|
||||
ptr->name = r_bin_name_new_from (r_str_newf ("0x%"PFMT64x": Transmission Control Protocol, Src Port: %d, Dst"
|
||||
" port: %d, Len: %d", paddr, tcp->src_port, tcp->dst_port, datasz));
|
||||
ptr->name = r_bin_name_new_from (r_str_newf ("0x%"PFMT64x": %s, Src Port: %d, Dst"
|
||||
" port: %d, Len: %d", paddr, name, rec->sport, rec->dport, rec->datasz));
|
||||
ptr->paddr = ptr->vaddr = paddr;
|
||||
}
|
||||
|
||||
|
|
@ -286,69 +341,34 @@ static void pcaprec_ipv4_sym_add(RVecRBinSymbol *vec, pcaprec_t* rec, ut64 paddr
|
|||
(ipv4->dst >> 24) & 0xFF, (ipv4->dst >> 16) & 0xFF,
|
||||
(ipv4->dst >> 8) & 0xFF, ipv4->dst & 0xFF));
|
||||
ptr->paddr = ptr->vaddr = paddr;
|
||||
|
||||
switch (ipv4->protocol) {
|
||||
case TRANSPORT_TCP:
|
||||
{
|
||||
ut32 tcpoff = ((ipv4->ver_len & 0x0F) * 4);
|
||||
pcaprec_tcp_sym_add (vec, rec, paddr + tcpoff, ipv4->tot_len - tcpoff);
|
||||
}
|
||||
break;
|
||||
#if 0
|
||||
case TRANSPORT_UDP:
|
||||
// TODO
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
pcaprec_transport_sym_add (vec, rec, paddr + ((ipv4->ver_len & 0x0F) * 4));
|
||||
}
|
||||
|
||||
static char *ipv6_addr_string(ut8 *addr) {
|
||||
size_t i;
|
||||
size_t start = -1;
|
||||
size_t tmp = -1;
|
||||
size_t len = 0, maxlen = 0;
|
||||
|
||||
ut16 words[8] = { 0 };
|
||||
static char *ipv6_addr_string(const ut8 *addr) {
|
||||
ut16 words[8];
|
||||
int i, start = -1, len = 0, maxlen = 0;
|
||||
// find the longest run of zero words, rfc5952 replaces it with "::"
|
||||
for (i = 0; i < 8; i++) {
|
||||
words[i] = r_read_at_be16 (addr, i * 2);
|
||||
}
|
||||
|
||||
// find the longest sequence of zero field
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (words[i] == 0) {
|
||||
if (tmp == -1) {
|
||||
tmp = i;
|
||||
len = 1;
|
||||
} else {
|
||||
len++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
len = words[i]? 0: len + 1;
|
||||
if (len > maxlen) {
|
||||
maxlen = len;
|
||||
start = tmp;
|
||||
start = i - len + 1;
|
||||
}
|
||||
tmp = -1;
|
||||
}
|
||||
|
||||
if (maxlen > 1) {
|
||||
RStrBuf *addr = r_strbuf_new (NULL);
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (i == start) {
|
||||
r_strbuf_append (addr, "::");
|
||||
i += maxlen - 1;
|
||||
} else {
|
||||
r_strbuf_appendf (addr, "%02x", words[i]);
|
||||
}
|
||||
if (maxlen < 2) {
|
||||
start = -1;
|
||||
}
|
||||
RStrBuf *sb = r_strbuf_new ("");
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (i == start) {
|
||||
r_strbuf_append (sb, "::");
|
||||
i += maxlen - 1;
|
||||
} else {
|
||||
r_strbuf_appendf (sb, "%s%x", (i > 0 && i != start + maxlen)? ":": "", words[i]);
|
||||
}
|
||||
return r_strbuf_drain (addr);
|
||||
}
|
||||
return r_str_newf ("%x:%x:%x:%x:%x:%x:%x:%x",
|
||||
words[0], words[1], words[2], words[3],
|
||||
words[4], words[5], words[6], words[7]);
|
||||
return r_strbuf_drain (sb);
|
||||
}
|
||||
|
||||
static void pcaprec_ipv6_sym_add(RVecRBinSymbol *vec, pcaprec_t* rec, ut64 paddr) {
|
||||
|
|
@ -360,19 +380,7 @@ static void pcaprec_ipv6_sym_add(RVecRBinSymbol *vec, pcaprec_t* rec, ut64 paddr
|
|||
ptr->paddr = ptr->vaddr = paddr;
|
||||
free (src);
|
||||
free (dst);
|
||||
|
||||
switch (ipv6->nxt) {
|
||||
case TRANSPORT_TCP:
|
||||
pcaprec_tcp_sym_add (vec, rec, paddr + sizeof (pcaprec_ipv6_t), ipv6->plen);
|
||||
break;
|
||||
#if 0
|
||||
case TRANSPORT_UDP:
|
||||
// TODO
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
pcaprec_transport_sym_add (vec, rec, paddr + sizeof (pcaprec_ipv6_t));
|
||||
}
|
||||
|
||||
void pcaprec_ether_sym_add(RVecRBinSymbol *vec, pcaprec_t *rec, ut64 paddr) {
|
||||
|
|
@ -490,3 +498,78 @@ const char* pcap_network_string(ut32 network) {
|
|||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
pcaprec_t *pcap_rec_at(pcap_obj_t *obj, ut64 addr) {
|
||||
RListIter *iter;
|
||||
pcaprec_t *rec;
|
||||
r_list_foreach (obj->recs, iter, rec) {
|
||||
if (addr >= rec->paddr && addr < rec_end (rec)) {
|
||||
return rec;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char *endpoint_string(pcap_stream_t *s, int ep) {
|
||||
const ut8 *ip = s->ip[ep];
|
||||
if (s->v6) {
|
||||
char *a = ipv6_addr_string (ip);
|
||||
char *r = r_str_newf ("[%s]:%d", a, s->port[ep]);
|
||||
free (a);
|
||||
return r;
|
||||
}
|
||||
return r_str_newf ("%d.%d.%d.%d:%d", ip[0], ip[1], ip[2], ip[3], s->port[ep]);
|
||||
}
|
||||
|
||||
char *pcap_stream_name(pcap_stream_t *s) {
|
||||
char *a = endpoint_string (s, 0);
|
||||
char *b = endpoint_string (s, 1);
|
||||
char *r = r_str_newf ("%s %s -> %s", s->proto == TRANSPORT_TCP? "tcp": "udp", a, b);
|
||||
free (a);
|
||||
free (b);
|
||||
return r;
|
||||
}
|
||||
|
||||
// payload sent in one direction, tcp retransmissions are dropped and overlaps trimmed
|
||||
ut8 *pcap_stream_data(pcap_obj_t *obj, pcap_stream_t *s, int dir, ut64 *len) {
|
||||
ut8 *buf = malloc (s->bytes[dir] + 1);
|
||||
if (!buf) {
|
||||
return NULL;
|
||||
}
|
||||
ut64 n = 0;
|
||||
ut32 next = 0;
|
||||
bool first = true;
|
||||
RListIter *iter;
|
||||
pcaprec_t *rec;
|
||||
r_list_foreach (s->recs, iter, rec) {
|
||||
if (rec->dir != dir || !rec->datasz) {
|
||||
continue;
|
||||
}
|
||||
ut64 off = rec->dataoff;
|
||||
ut32 sz = rec->datasz;
|
||||
if (s->proto == TRANSPORT_TCP) {
|
||||
if (first) {
|
||||
next = rec->seq;
|
||||
first = false;
|
||||
}
|
||||
st32 delta = (st32)(rec->seq - next);
|
||||
if (delta < 0) {
|
||||
if ((ut32)-delta >= sz) {
|
||||
continue;
|
||||
}
|
||||
off -= delta;
|
||||
sz += delta;
|
||||
}
|
||||
if ((st32)(rec->seq + rec->datasz - next) > 0) {
|
||||
next = rec->seq + rec->datasz;
|
||||
}
|
||||
}
|
||||
st64 r = r_buf_read_at (obj->b, off, buf + n, sz);
|
||||
if (r > 0) {
|
||||
n += r;
|
||||
}
|
||||
}
|
||||
buf[n] = 0;
|
||||
*len = n;
|
||||
return buf;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@
|
|||
#define NET_IPV6 0x86dd
|
||||
|
||||
#define TRANSPORT_TCP 6
|
||||
#define TRANSPORT_UDP 17
|
||||
|
||||
// Global Header
|
||||
typedef struct pcap_hdr_s {
|
||||
|
|
@ -103,20 +104,6 @@ typedef struct pcaprec_ipv6 {
|
|||
ut8 dst[16]; // destination address
|
||||
} pcaprec_ipv6_t;
|
||||
|
||||
// TCP header, 20 - 60 bytes
|
||||
typedef struct pcaprec_tcp {
|
||||
ut16 src_port; // Port on source
|
||||
ut16 dst_port; // Port on destination
|
||||
ut32 seq_num; // Sequence number
|
||||
ut32 ack_num; // Ack number
|
||||
ut8 hdr_len; // Length of TCP header
|
||||
ut16 flags; // TCP flags
|
||||
ut16 win_sz; // Window size
|
||||
ut16 chksum;
|
||||
ut16 urgnt_ptr; // Urgent
|
||||
// Variable length options. Use hdr_len
|
||||
} pcaprec_tcp_t;
|
||||
|
||||
// Record (Packet) Header
|
||||
typedef struct pcaprec_hdr_s {
|
||||
ut32 ts_sec; // Timestamp in seconds
|
||||
|
|
@ -125,6 +112,17 @@ typedef struct pcaprec_hdr_s {
|
|||
ut32 orig_len; // Original length of packet
|
||||
} pcaprec_hdr_t;
|
||||
|
||||
// A reconstructed tcp/udp conversation between two endpoints
|
||||
typedef struct pcap_stream_s {
|
||||
int id;
|
||||
ut8 proto; // TRANSPORT_TCP or TRANSPORT_UDP
|
||||
bool v6;
|
||||
ut8 ip[2][16]; // endpoint addresses, ipv4 uses the first 4 bytes
|
||||
ut16 port[2];
|
||||
ut64 bytes[2]; // payload bytes sent by each endpoint
|
||||
RList/*<pcaprec_t>*/ *recs; // packets in capture order, not owned
|
||||
} pcap_stream_t;
|
||||
|
||||
typedef struct pcaprec_s {
|
||||
ut64 paddr;
|
||||
pcaprec_hdr_t *hdr;
|
||||
|
|
@ -135,17 +133,23 @@ typedef struct pcaprec_s {
|
|||
pcaprec_ipv4_t *ipv4_hdr;
|
||||
pcaprec_ipv6_t *ipv6_hdr;
|
||||
} net;
|
||||
union {
|
||||
pcaprec_tcp_t *tcp_hdr;
|
||||
} transport;
|
||||
ut32 datasz;
|
||||
ut8 *data;
|
||||
bool v6;
|
||||
ut8 proto; // transport protocol, 0 if unknown
|
||||
ut16 sport;
|
||||
ut16 dport;
|
||||
ut32 seq; // tcp sequence number
|
||||
ut64 dataoff; // paddr of the transport payload
|
||||
ut32 datasz; // captured payload bytes
|
||||
pcap_stream_t *stream;
|
||||
int dir; // 0 if sent by stream->ip[0] (the initiator), 1 otherwise
|
||||
} pcaprec_t;
|
||||
|
||||
// The pcap object for RBinFile
|
||||
typedef struct pcap_obj_s {
|
||||
pcap_hdr_t *header; // File header
|
||||
RList/*<pcaprec_t>*/ *recs;
|
||||
RList/*<pcap_stream_t>*/ *streams;
|
||||
int cur; // selected stream
|
||||
bool is_nsec; // nsec timestamp resolution?
|
||||
bool bigendian;
|
||||
RBuffer *b;
|
||||
|
|
@ -157,5 +161,8 @@ void pcaprec_free(pcaprec_t *rec);
|
|||
void pcaprec_frame_sym_add(RVecRBinSymbol *vec, pcaprec_t *rec, int n);
|
||||
void pcaprec_ether_sym_add(RVecRBinSymbol *vec, pcaprec_t *rec, ut64 paddr);
|
||||
const char* pcap_network_string(ut32 network);
|
||||
pcaprec_t *pcap_rec_at(pcap_obj_t *obj, ut64 addr);
|
||||
char *pcap_stream_name(pcap_stream_t *s);
|
||||
ut8 *pcap_stream_data(pcap_obj_t *obj, pcap_stream_t *s, int dir, ut64 *len);
|
||||
|
||||
#endif // _PCAP_H_
|
||||
|
|
|
|||
|
|
@ -5,8 +5,6 @@
|
|||
|
||||
#include "../format/pcap/pcap.h"
|
||||
|
||||
#define CUSTOM_STRINGS 0
|
||||
|
||||
static RBinInfo *info(RBinFile *bf) {
|
||||
R_RETURN_VAL_IF_FAIL (bf && bf->bo && bf->bo->bin_obj, NULL);
|
||||
RBinInfo *ret = R_NEW0 (RBinInfo);
|
||||
|
|
@ -42,6 +40,10 @@ static bool load(RBinFile *bf, RBuffer *buf, ut64 loadaddr) {
|
|||
return bf->bo->bin_obj != NULL;
|
||||
}
|
||||
|
||||
static void destroy(RBinFile *bf) {
|
||||
pcap_obj_free (bf->bo->bin_obj);
|
||||
}
|
||||
|
||||
static bool symbols_vec(RBinFile *bf) {
|
||||
R_RETURN_VAL_IF_FAIL (bf && bf->bo && bf->bo->bin_obj, false);
|
||||
|
||||
|
|
@ -79,37 +81,6 @@ static bool symbols_vec(RBinFile *bf) {
|
|||
return true;
|
||||
}
|
||||
|
||||
#if CUSTOM_STRINGS
|
||||
static RVecRBinString *strings(RBinFile *bf) {
|
||||
R_RETURN_VAL_IF_FAIL (bf && bf->bo && bf->bo->bin_obj, NULL);
|
||||
|
||||
pcap_obj_t *obj = bf->bo->bin_obj;
|
||||
RVecRBinString *ret = RVecRBinString_new ();
|
||||
|
||||
RListIter *iter;
|
||||
pcaprec_t *rec;
|
||||
r_list_foreach (obj->recs, iter, rec) {
|
||||
if (rec->data && *rec->data != 0) {
|
||||
RBinString *ptr = RVecRBinString_emplace_back (ret);
|
||||
if (!ptr) {
|
||||
break;
|
||||
}
|
||||
ptr->string = r_str_ndup ((const char *)rec->data, 32); // rec->datasz);
|
||||
if (strlen (ptr->string) < 10) {
|
||||
// eprintf ("(%s)\n", ptr->string);
|
||||
RVecRBinString_pop_back (ret);
|
||||
continue;
|
||||
}
|
||||
ptr->paddr = ptr->vaddr = rec->paddr; //XXX;
|
||||
ptr->length = strlen (ptr->string);
|
||||
ptr->size = ptr->length + 1;
|
||||
ptr->type = R_STRING_TYPE_DETECT;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static RList* libs(RBinFile *bf) {
|
||||
R_RETURN_VAL_IF_FAIL (bf && bf->bo && bf->bo->bin_obj, NULL);
|
||||
RList *ret = r_list_newf (free);
|
||||
|
|
@ -124,6 +95,143 @@ static ut64 baddr(RBinFile *bf) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void stream_print(RBin *bin, pcap_obj_t *obj, pcap_stream_t *s) {
|
||||
char *name = pcap_stream_name (s);
|
||||
bin->cb_printf ("%c %d %s pkts=%d a=%"PFMT64u" b=%"PFMT64u"\n", s->id == obj->cur? '*': ' ',
|
||||
s->id, name, r_list_length (s->recs), s->bytes[0], s->bytes[1]);
|
||||
free (name);
|
||||
}
|
||||
|
||||
static void message_print(RBin *bin, pcaprec_t *rec) {
|
||||
bin->cb_printf ("0x%08"PFMT64x" %c %d\n", rec->dataoff, rec->dir? '<': '>', rec->datasz);
|
||||
}
|
||||
|
||||
static bool stream_select(RBin *bin, pcap_obj_t *obj, int id) {
|
||||
pcap_stream_t *s = r_list_get_n (obj->streams, id);
|
||||
if (!s) {
|
||||
R_LOG_ERROR ("Invalid stream number");
|
||||
return false;
|
||||
}
|
||||
obj->cur = id;
|
||||
stream_print (bin, obj, s);
|
||||
return true;
|
||||
}
|
||||
|
||||
// seek to the next or previous packet with payload in the stream of the current packet
|
||||
static bool message_seek(RBin *bin, pcap_obj_t *obj, pcap_stream_t *s, bool next) {
|
||||
RIO *io = bin->iob.io;
|
||||
void *core = io? io->coreb.core: NULL;
|
||||
if (!core) {
|
||||
R_LOG_ERROR ("Seeking requires a core");
|
||||
return false;
|
||||
}
|
||||
ut64 addr = io->coreb.numGet (core, "$$");
|
||||
pcaprec_t *cur = pcap_rec_at (obj, addr);
|
||||
if (cur) {
|
||||
addr = cur->paddr;
|
||||
if (cur->stream) {
|
||||
s = cur->stream;
|
||||
}
|
||||
}
|
||||
if (!s) {
|
||||
R_LOG_ERROR ("No stream at the current offset");
|
||||
return true;
|
||||
}
|
||||
pcaprec_t *hit = NULL;
|
||||
RListIter *iter;
|
||||
pcaprec_t *rec;
|
||||
r_list_foreach (s->recs, iter, rec) {
|
||||
if (!rec->datasz) {
|
||||
continue;
|
||||
}
|
||||
if (rec->paddr > addr) {
|
||||
if (next) {
|
||||
hit = rec;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (rec->paddr < addr) {
|
||||
hit = rec;
|
||||
}
|
||||
}
|
||||
if (hit) {
|
||||
io->coreb.cmdf (core, "s 0x%"PFMT64x, hit->dataoff);
|
||||
} else {
|
||||
R_LOG_WARN ("No %s message in stream %d", next? "next": "previous", s->id);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool pcap_cmd(RBinFile *bf, const char *cmd) {
|
||||
R_RETURN_VAL_IF_FAIL (bf && bf->bo && bf->bo->bin_obj && cmd, false);
|
||||
pcap_obj_t *obj = bf->bo->bin_obj;
|
||||
RBin *bin = bf->rbin;
|
||||
pcap_stream_t *s = r_list_get_n (obj->streams, obj->cur);
|
||||
RListIter *iter;
|
||||
pcaprec_t *rec;
|
||||
switch (*cmd) {
|
||||
case 'n': // "i:n"
|
||||
case 'p': // "i:p"
|
||||
return message_seek (bin, obj, s, *cmd == 'n');
|
||||
case 's': // "i:s"
|
||||
if (cmd[1] != ' ' && !s) {
|
||||
R_LOG_ERROR ("No streams found");
|
||||
return false;
|
||||
}
|
||||
switch (cmd[1]) {
|
||||
case 0: // "i:s"
|
||||
r_list_foreach (obj->streams, iter, s) {
|
||||
stream_print (bin, obj, s);
|
||||
}
|
||||
return true;
|
||||
case ' ': // "i:s 3"
|
||||
return stream_select (bin, obj, r_num_math (NULL, cmd + 2));
|
||||
case '.': // "i:s."
|
||||
{
|
||||
RIO *io = bin->iob.io;
|
||||
rec = io && io->coreb.core? pcap_rec_at (obj, io->coreb.numGet (io->coreb.core, "$$")): NULL;
|
||||
if (!rec || !rec->stream) {
|
||||
R_LOG_ERROR ("No stream at the current offset");
|
||||
return false;
|
||||
}
|
||||
return stream_select (bin, obj, rec->stream->id);
|
||||
}
|
||||
case 'p': // "i:sp"
|
||||
r_list_foreach (s->recs, iter, rec) {
|
||||
if (rec->datasz) {
|
||||
message_print (bin, rec);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
case 'a': // "i:sa"
|
||||
case 'b': // "i:sb"
|
||||
{
|
||||
ut64 len;
|
||||
ut8 *data = pcap_stream_data (obj, s, cmd[1] == 'b', &len);
|
||||
if (data && len > 0) {
|
||||
bin->consb.cb_write (bin->consb.cons, data, len);
|
||||
}
|
||||
free (data);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// fallthrough
|
||||
case 0:
|
||||
case '?':
|
||||
bin->cb_printf ("Usage: i:[snp] reconstruct tcp/udp streams\n"
|
||||
"| i:s list the streams, '*' marks the selected one\n"
|
||||
"| i:s <n> select the stream number <n>\n"
|
||||
"| i:s. select the stream of the packet at the current offset\n"
|
||||
"| i:sp list the messages (payload offset, direction, size) of the selected stream\n"
|
||||
"| i:sa dump the payload sent by the stream initiator (a -> b)\n"
|
||||
"| i:sb dump the payload sent by the stream responder (b -> a)\n"
|
||||
"| i:n seek to the next message in the stream of the current packet\n"
|
||||
"| i:p seek to the previous message in the stream of the current packet\n");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
RBinPlugin r_bin_plugin_pcap = {
|
||||
.meta = {
|
||||
.name = "pcap",
|
||||
|
|
@ -135,11 +243,10 @@ RBinPlugin r_bin_plugin_pcap = {
|
|||
.libs = libs,
|
||||
.baddr = baddr,
|
||||
.minstrlen = 16,
|
||||
#if CUSTOM_STRINGS
|
||||
.strings = strings,
|
||||
#endif
|
||||
.symbols_vec = symbols_vec,
|
||||
.load= load,
|
||||
.cmd = pcap_cmd,
|
||||
.load = load,
|
||||
.destroy = destroy,
|
||||
.check = check,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -3618,6 +3618,11 @@ static int cmd_info(void *data, const char *input) {
|
|||
case 'e': // "ie?"
|
||||
r_cons_cmd_help (core->cons, help_msg_ie);
|
||||
break;
|
||||
case ':': // "i:?"
|
||||
if (r_bin_cmd (core->bin, "?")) {
|
||||
break;
|
||||
}
|
||||
// fallthrough
|
||||
default:
|
||||
r_cons_cmd_help_match (core->cons, help_msg_i, cmd, 0, false);
|
||||
break;
|
||||
|
|
@ -3937,7 +3942,7 @@ static int cmd_info(void *data, const char *input) {
|
|||
cmd_info_demangle (core, input, pj, mode);
|
||||
break;
|
||||
case ':': // "i:"
|
||||
if (!r_bin_cmd (core->bin, input)) {
|
||||
if (!r_bin_cmd (core->bin, input + 1)) {
|
||||
R_LOG_ERROR ("Unhandled RBinPlugin.cmd");
|
||||
}
|
||||
break;
|
||||
|
|
|
|||
128
test/db/formats/pcap
Normal file
128
test/db/formats/pcap
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
NAME=pcap stream list
|
||||
FILE=bins/pcap/udp6.pcap
|
||||
CMDS=<<EOF
|
||||
i:s
|
||||
i:s 1
|
||||
i:s
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
* 0 udp 10.0.0.1:5353 -> 10.0.0.2:53 pkts=3 a=11 b=7
|
||||
1 tcp [fe80::1]:4000 -> [2001:db8::]:80 pkts=4 a=43 b=16
|
||||
* 1 tcp [fe80::1]:4000 -> [2001:db8::]:80 pkts=4 a=43 b=16
|
||||
0 udp 10.0.0.1:5353 -> 10.0.0.2:53 pkts=3 a=11 b=7
|
||||
* 1 tcp [fe80::1]:4000 -> [2001:db8::]:80 pkts=4 a=43 b=16
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=pcap udp stream dump
|
||||
FILE=bins/pcap/udp6.pcap
|
||||
CMDS=<<EOF
|
||||
i:sa
|
||||
?e
|
||||
i:sb
|
||||
?e
|
||||
i:sp
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
query?again
|
||||
answer!
|
||||
0x00000052 > 6
|
||||
0x00000092 < 7
|
||||
0x00000276 > 5
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=pcap tcp stream reassembly
|
||||
FILE=bins/pcap/udp6.pcap
|
||||
CMDS=<<EOF
|
||||
i:s 1~?
|
||||
i:sa
|
||||
i:sb
|
||||
i:sp
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
1
|
||||
GET / HTTP/1.0
|
||||
|
||||
extra
|
||||
HTTP/1.0 200 OK
|
||||
0x000000f3 > 16
|
||||
0x0000015d > 16
|
||||
0x000001c7 < 16
|
||||
0x00000231 > 11
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=pcap stream navigation
|
||||
FILE=bins/pcap/udp6.pcap
|
||||
CMDS=<<EOF
|
||||
s 0x99
|
||||
i:n
|
||||
?v $$
|
||||
i:n
|
||||
?v $$
|
||||
i:n
|
||||
?v $$
|
||||
i:p
|
||||
?v $$
|
||||
i:s.~?
|
||||
i:s~*
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
0x15d
|
||||
0x1c7
|
||||
0x231
|
||||
0x1c7
|
||||
1
|
||||
* 1 tcp [fe80::1]:4000 -> [2001:db8::]:80 pkts=4 a=43 b=16
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=pcap transport symbols
|
||||
FILE=bins/pcap/udp6.pcap
|
||||
CMDS=<<EOF
|
||||
isq~Protocol
|
||||
isq~IPV6
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
0x0000004a 0 0x4a: User Datagram Protocol, Src Port: 5353, Dst port: 53, Len: 6
|
||||
0x0000008a 0 0x8a: User Datagram Protocol, Src Port: 53, Dst port: 5353, Len: 7
|
||||
0x000000df 0 0xdf: Transmission Control Protocol, Src Port: 4000, Dst port: 80, Len: 16
|
||||
0x00000149 0 0x149: Transmission Control Protocol, Src Port: 4000, Dst port: 80, Len: 16
|
||||
0x000001b3 0 0x1b3: Transmission Control Protocol, Src Port: 80, Dst port: 4000, Len: 16
|
||||
0x0000021d 0 0x21d: Transmission Control Protocol, Src Port: 4000, Dst port: 80, Len: 11
|
||||
0x0000026e 0 0x26e: User Datagram Protocol, Src Port: 5353, Dst port: 53, Len: 5
|
||||
0x000000b7 0 0xb7: IPV6, Src: fe80::1, Dst: 2001:db8::
|
||||
0x00000121 0 0x121: IPV6, Src: fe80::1, Dst: 2001:db8::
|
||||
0x0000018b 0 0x18b: IPV6, Src: 2001:db8::, Dst: fe80::1
|
||||
0x000001f5 0 0x1f5: IPV6, Src: fe80::1, Dst: 2001:db8::
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=pcap gdb session stream
|
||||
FILE=bins/pcap/sample0.pcap
|
||||
CMDS=<<EOF
|
||||
i:s~?
|
||||
i:s 3
|
||||
i:sp~?
|
||||
i:sb~<?xml?
|
||||
s 0x3f5
|
||||
i:s.~?
|
||||
i:n
|
||||
?v $$
|
||||
i:p
|
||||
i:p
|
||||
?v $$
|
||||
ps
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
32
|
||||
* 3 tcp 127.0.0.1:49326 -> 127.0.0.1:4444 pkts=825 a=9287 b=789331
|
||||
807
|
||||
9
|
||||
1
|
||||
0x538
|
||||
0x350
|
||||
+m
|
||||
EOF
|
||||
RUN
|
||||
Loading…
Add table
Add a link
Reference in a new issue