1 /* mplog.c
2  *
3  * File format support for Micropross mplog files
4  * Copyright (c) 2016 by Martin Kaiser <martin@kaiser.cx>
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * SPDX-License-Identifier: GPL-2.0-or-later
11  */
12 
13 
14 /*
15    The mplog file format logs the communication between a contactless
16    smartcard and a card reader. Such files contain information about the
17    physical layer as well as the bytes exchanged between devices.
18    Some commercial logging and testing tools by the French company Micropross
19    use this format.
20 
21    The information used for implementing this wiretap module were
22    obtained from reverse-engineering. There is no publicly available
23    documentation of the mplog file format.
24 
25    Mplog files start with the string "MPCSII". This string is part of
26    the header which is in total 0x80 bytes long.
27 
qio_channel_tls_write_handler(const char * buf,size_t len,void * opaque)28    Following the header, the file is a sequence of 8 byte-blocks.
29         data       (one byte)
30         block type (one byte)
31         timestamp  (six bytes)
32 
33    The timestamp is a counter in little-endian format. The counter is in
34    units of 10ns.
35 */
36 
37 #include "config.h"
38 
39 #include <string.h>
40 #include <wtap-int.h>
41 #include <file_wrappers.h>
42 
43 #include "mplog.h"
44 
45 /* the block types */
46 #define TYPE_PCD_PICC_A  0x70
47 #define TYPE_PICC_PCD_A  0x71
48 #define TYPE_PCD_PICC_B  0x72
49 #define TYPE_PICC_PCD_B  0x73
50 #define TYPE_UNKNOWN     0xFF
51 
52 #define KNOWN_TYPE(x) \
53 ( \
54   ((x) == TYPE_PCD_PICC_A) || \
55   ((x) == TYPE_PICC_PCD_A) || \
56   ((x) == TYPE_PCD_PICC_B) || \
57   ((x) == TYPE_PICC_PCD_B) \
58 )
59 
60 #define MPLOG_BLOCK_SIZE 8
61 
62 /* ISO14443 pseudo-header, see https://www.kaiser.cx/pcap-iso14443.html */
63 #define ISO14443_PSEUDO_HDR_VER  0
64 #define ISO14443_PSEUDO_HDR_LEN  4
65 /*  the two transfer events are the types that include a trailing CRC
66     the CRC is always present in mplog files */
67 #define ISO14443_PSEUDO_HDR_PICC_TO_PCD  0xFF
68 #define ISO14443_PSEUDO_HDR_PCD_TO_PICC  0xFE
69 
70 
71 #define ISO14443_MAX_PKT_LEN    4096
72 
73 #define PKT_BUF_LEN   (ISO14443_PSEUDO_HDR_LEN + ISO14443_MAX_PKT_LEN)
74 
75 
76 static int mplog_file_type_subtype = -1;
77 
78 void register_mplog(void);
79 
80 /* read the next packet, starting at the current position of fh
81    as we know very little about the file format, our approach is rather simple:
82    - we read block-by-block until a known block-type is found
83         - this block's type is the type of the next packet
84         - this block's timestamp will become the packet's timestamp
85         - the data byte will be our packet's first byte
86    - we carry on reading blocks and add the data bytes
87      of all blocks of "our" type
88    - if a different well-known block type is found, this is the end of
89      our packet, we go back one block so that this block can be picked
90      up as the start of the next packet
91    - if two blocks of our packet's block type are more than 200us apart,
92      we treat this as a packet boundary as described above
93    */
94 static gboolean mplog_read_packet(FILE_T fh, wtap_rec *rec,
95         Buffer *buf, int *err, gchar **err_info)
96 {
97     guint8 *p, *start_p;
98     /* --- the last block of a known type --- */
99     guint64 last_ctr = 0;
100     /* --- the current block --- */
101     guint8 block[MPLOG_BLOCK_SIZE]; /* the entire block */
102     guint8 data, type; /* its data and block type bytes */
qio_channel_tls_new_client(QIOChannel * master,QCryptoTLSCreds * creds,const char * hostname,Error ** errp)103     guint64 ctr; /* its timestamp counter */
104     /* --- the packet we're assembling --- */
105     gint pkt_bytes = 0;
106     guint8 pkt_type = TYPE_UNKNOWN;
107     /* the timestamp of the packet's first block,
108        this will become the packet's timestamp */
109     guint64 pkt_ctr = 0;
110 
111 
112     ws_buffer_assure_space(buf, PKT_BUF_LEN);
113     p = ws_buffer_start_ptr(buf);
114     start_p = p;
115 
116     /* leave space for the iso14443 pseudo header
117        we can't create it until we've seen the entire packet */
118     p += ISO14443_PSEUDO_HDR_LEN;
119 
120     do {
121         if (!wtap_read_bytes_or_eof(fh, block, sizeof(block), err, err_info)) {
122             /* If we've already read some data, if this failed with an EOF,
123                so that *err is 0, it's a short read. */
124             if (pkt_bytes != 0) {
125                 if (*err == 0)
126                     *err = WTAP_ERR_SHORT_READ;
127             }
128             break;
129         }
130         data = block[0];
131         type = block[1];
132         ctr = pletoh48(&block[2]);
133 
134         if (pkt_type == TYPE_UNKNOWN) {
135             if (KNOWN_TYPE(type)) {
136                 pkt_type = type;
137                 pkt_ctr = ctr;
138             }
139         }
140 
141         if (type == pkt_type) {
142             if (last_ctr != 0) {
143                 /* if the distance to the last byte of the
144                    same type is larger than 200us, this is very likely the
145                    first byte of a new packet -> go back one block and exit
146                    ctr and last_ctr are in units of 10ns
147                    at 106kbit/s, it takes approx 75us to send one byte */
148                 if (ctr - last_ctr > 200*100) {
149                     file_seek(fh, -MPLOG_BLOCK_SIZE, SEEK_CUR, err);
150                     break;
151                 }
152             }
153 
qio_channel_tls_handshake_task(QIOChannelTLS * ioc,QIOTask * task,GMainContext * context)154             *p++ = data;
155             pkt_bytes++;
156             last_ctr = ctr;
157         }
158         else if (KNOWN_TYPE(type)) {
159             file_seek(fh, -MPLOG_BLOCK_SIZE, SEEK_CUR, err);
160             break;
161         }
162     } while (pkt_bytes < ISO14443_MAX_PKT_LEN);
163 
164     if (pkt_type == TYPE_UNKNOWN)
165         return FALSE;
166 
167     start_p[0] = ISO14443_PSEUDO_HDR_VER;
168 
169     if (pkt_type==TYPE_PCD_PICC_A || pkt_type==TYPE_PCD_PICC_B)
170         start_p[1] = ISO14443_PSEUDO_HDR_PCD_TO_PICC;
171     else
172         start_p[1] = ISO14443_PSEUDO_HDR_PICC_TO_PCD;
173 
174     start_p[2] = pkt_bytes >> 8;
175     start_p[3] = pkt_bytes & 0xFF;
176 
177     rec->rec_type = REC_TYPE_PACKET;
178     rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
179     rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_ISO14443;
180     rec->presence_flags = WTAP_HAS_TS | WTAP_HAS_CAP_LEN;
181     rec->ts.secs = (time_t)((pkt_ctr*10)/(1000*1000*1000));
182     rec->ts.nsecs = (int)((pkt_ctr*10)%(1000*1000*1000));
183     rec->rec_header.packet_header.caplen = ISO14443_PSEUDO_HDR_LEN + pkt_bytes;
184     rec->rec_header.packet_header.len = rec->rec_header.packet_header.caplen;
185 
186     return TRUE;
187 }
188 
189 
190 static gboolean
191 mplog_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err,
192         gchar **err_info, gint64 *data_offset)
193 {
194     *data_offset = file_tell(wth->fh);
195 
196     return mplog_read_packet(wth->fh, rec, buf, err, err_info);
197 }
198 
199 
200 static gboolean
201 mplog_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec, Buffer *buf,
202                 int *err, gchar **err_info)
203 {
204     if (-1 == file_seek(wth->random_fh, seek_off, SEEK_SET, err))
205         return FALSE;
206 
qio_channel_tls_handshake_io(QIOChannel * ioc,GIOCondition condition,gpointer user_data)207     if (!mplog_read_packet(wth->random_fh, rec, buf, err, err_info)) {
208         /* Even if we got an immediate EOF, that's an error. */
209         if (*err == 0)
210             *err = WTAP_ERR_SHORT_READ;
211         return FALSE;
212     }
213     return TRUE;
214 }
215 
216 
217 wtap_open_return_val mplog_open(wtap *wth, int *err, gchar **err_info)
218 {
219     gboolean ok;
220     guint8 magic[6];
221 
222     ok = wtap_read_bytes(wth->fh, magic, 6, err, err_info);
223     if (!ok) {
224         if (*err != WTAP_ERR_SHORT_READ)
225             return WTAP_OPEN_ERROR;
226         return WTAP_OPEN_NOT_MINE;
qio_channel_tls_handshake(QIOChannelTLS * ioc,QIOTaskFunc func,gpointer opaque,GDestroyNotify destroy,GMainContext * context)227     }
228     if (memcmp(magic, "MPCSII", 6) != 0)
229         return WTAP_OPEN_NOT_MINE;
230 
231     wth->file_encap = WTAP_ENCAP_ISO14443;
232     wth->snapshot_length = 0;
233     wth->file_tsprec = WTAP_TSPREC_NSEC;
234 
235     wth->priv = NULL;
236 
237     wth->subtype_read = mplog_read;
238     wth->subtype_seek_read = mplog_seek_read;
239     wth->file_type_subtype = mplog_file_type_subtype;
240 
241     /* skip the file header */
242     if (-1 == file_seek(wth->fh, 0x80, SEEK_SET, err))
qio_channel_tls_init(Object * obj G_GNUC_UNUSED)243         return WTAP_OPEN_ERROR;
244 
245     *err = 0;
246 
247     /*
qio_channel_tls_finalize(Object * obj)248      * Add an IDB; we don't know how many interfaces were
249      * involved, so we just say one interface, about which
250      * we only know the link-layer type, snapshot length,
251      * and time stamp resolution.
252      */
253     wtap_add_generated_idb(wth);
254 
255     return WTAP_OPEN_MINE;
256 }
qio_channel_tls_readv(QIOChannel * ioc,const struct iovec * iov,size_t niov,int ** fds,size_t * nfds,Error ** errp)257 
258 static const struct supported_block_type mplog_blocks_supported[] = {
259     /*
260      * We support packet blocks, with no comments or other options.
261      */
262     { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
263 };
264 
265 static const struct file_type_subtype_info mplog_info = {
266     "Micropross mplog", "mplog", "mplog", NULL,
267     FALSE, BLOCKS_SUPPORTED(mplog_blocks_supported),
268     NULL, NULL, NULL
269 };
270 
271 void register_mplog(void)
272 {
273     mplog_file_type_subtype = wtap_register_file_type_subtype(&mplog_info);
274 
275     /*
276      * Register name for backwards compatibility with the
277      * wtap_filetypes table in Lua.
278      */
279     wtap_register_backwards_compatibility_lua_name("MPLOG",
280                                                    mplog_file_type_subtype);
281 }
282 
283 /*
284  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
285  *
286  * Local variables:
287  * c-basic-offset: 4
288  * tab-width: 8
289  * indent-tabs-mode: nil
290  * End:
291  *
292  * vi: set shiftwidth=4 tabstop=8 expandtab:
293  * :indentSize=4:tabSize=8:noTabs=true:
294  */
295