Fix #25480 - pcap stream selection support ##bin

This commit is contained in:
pancake 2026-08-22 13:08:52 +02:00
parent 9f345aaaf8
commit b4e48fb498
5 changed files with 533 additions and 203 deletions

View file

@ -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;
}

View file

@ -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_

View file

@ -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,
};

View file

@ -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
View 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