3 * File format support for Micropross mplog files
4 * Copyright (c) 2016 by Martin Kaiser <martin@kaiser.cx>
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * SPDX-License-Identifier: GPL-2.0-or-later
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
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.
25 Mplog files start with the string "MPCSII". This string is part of
26 the header which is in total 0x80 bytes long.
28 Following the header, the file is a sequence of 8 byte-blocks.
33 The timestamp is a counter in little-endian format. The counter is in
41 #include <file_wrappers.h>
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
52 #define KNOWN_TYPE(x) \
54 ((x) == TYPE_PCD_PICC_A) || \
55 ((x) == TYPE_PICC_PCD_A) || \
56 ((x) == TYPE_PCD_PICC_B) || \
57 ((x) == TYPE_PICC_PCD_B) \
60 #define MPLOG_BLOCK_SIZE 8
62 /* ISO14443 pseudo-header, see http://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
71 #define ISO14443_MAX_PKT_LEN 256
73 #define PKT_BUF_LEN (ISO14443_PSEUDO_HDR_LEN + ISO14443_MAX_PKT_LEN)
76 /* read the next packet, starting at the current position of fh
77 as we know very little about the file format, our approach is rather simple:
78 - we read block-by-block until a known block-type is found
79 - this block's type is the type of the next packet
80 - this block's timestamp will become the packet's timestamp
81 - the data byte will be our packet's first byte
82 - we carry on reading blocks and add the data bytes
83 of all blocks of "our" type
84 - if a different well-known block type is found, this is the end of
85 our packet, we go back one block so that this block can be picked
86 up as the start of the next packet
87 - if two blocks of our packet's block type are more than 200us apart,
88 we treat this as a packet boundary as described above
90 static gboolean mplog_read_packet(FILE_T fh, wtap_rec *rec,
91 Buffer *buf, int *err, gchar **err_info)
94 /* --- the last block of a known type --- */
96 /* --- the current block --- */
97 guint8 block[MPLOG_BLOCK_SIZE]; /* the entire block */
98 guint8 data, type; /* its data and block type bytes */
99 guint64 ctr; /* its timestamp counter */
100 /* --- the packet we're assembling --- */
102 guint8 pkt_type = TYPE_UNKNOWN;
103 /* the timestamp of the packet's first block,
104 this will become the packet's timestamp */
108 ws_buffer_assure_space(buf, PKT_BUF_LEN);
109 p = ws_buffer_start_ptr(buf);
112 /* leave space for the iso14443 pseudo header
113 we can't create it until we've seen the entire packet */
114 p += ISO14443_PSEUDO_HDR_LEN;
117 if (!wtap_read_bytes_or_eof(fh, block, sizeof(block), err, err_info)) {
118 /* If we've already read some data, if this failed with an EOF,
119 so that *err is 0, it's a short read. */
120 if (pkt_bytes != 0) {
122 *err = WTAP_ERR_SHORT_READ;
128 ctr = pletoh48(&block[2]);
130 if (pkt_type == TYPE_UNKNOWN) {
131 if (KNOWN_TYPE(type)) {
137 if (type == pkt_type) {
139 /* if the distance to the last byte of the
140 same type is larger than 200us, this is very likely the
141 first byte of a new packet -> go back one block and exit
142 ctr and last_ctr are in units of 10ns
143 at 106kbit/s, it takes approx 75us to send one byte */
144 if (ctr - last_ctr > 200*100) {
145 file_seek(fh, -MPLOG_BLOCK_SIZE, SEEK_CUR, err);
154 else if (KNOWN_TYPE(type)) {
155 file_seek(fh, -MPLOG_BLOCK_SIZE, SEEK_CUR, err);
158 } while (pkt_bytes < ISO14443_MAX_PKT_LEN);
160 if (pkt_type == TYPE_UNKNOWN)
163 start_p[0] = ISO14443_PSEUDO_HDR_VER;
165 if (pkt_type==TYPE_PCD_PICC_A || pkt_type==TYPE_PCD_PICC_B)
166 start_p[1] = ISO14443_PSEUDO_HDR_PCD_TO_PICC;
168 start_p[1] = ISO14443_PSEUDO_HDR_PICC_TO_PCD;
170 start_p[2] = pkt_bytes >> 8;
171 start_p[3] = pkt_bytes & 0xFF;
173 rec->rec_type = REC_TYPE_PACKET;
174 rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_ISO14443;
175 rec->presence_flags = WTAP_HAS_TS | WTAP_HAS_CAP_LEN;
176 rec->ts.secs = (time_t)((pkt_ctr*10)/(1000*1000*1000));
177 rec->ts.nsecs = (int)((pkt_ctr*10)%(1000*1000*1000));
178 rec->rec_header.packet_header.caplen = ISO14443_PSEUDO_HDR_LEN + pkt_bytes;
179 rec->rec_header.packet_header.len = rec->rec_header.packet_header.caplen;
186 mplog_read(wtap *wth, int *err, gchar **err_info, gint64 *data_offset)
188 *data_offset = file_tell(wth->fh);
190 return mplog_read_packet(
191 wth->fh, &wth->rec, wth->rec_data, err, err_info);
196 mplog_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec, Buffer *buf,
197 int *err, gchar **err_info)
199 if (-1 == file_seek(wth->random_fh, seek_off, SEEK_SET, err))
202 if (!mplog_read_packet(wth->random_fh, rec, buf, err, err_info)) {
203 /* Even if we got an immediate EOF, that's an error. */
205 *err = WTAP_ERR_SHORT_READ;
212 wtap_open_return_val mplog_open(wtap *wth, int *err, gchar **err_info)
217 ok = wtap_read_bytes(wth->fh, magic, 6, err, err_info);
219 if (*err != WTAP_ERR_SHORT_READ)
220 return WTAP_OPEN_ERROR;
221 return WTAP_OPEN_NOT_MINE;
223 if (memcmp(magic, "MPCSII", 6) != 0)
224 return WTAP_OPEN_NOT_MINE;
226 wth->file_encap = WTAP_ENCAP_ISO14443;
227 wth->snapshot_length = 0;
228 wth->file_tsprec = WTAP_TSPREC_NSEC;
232 wth->subtype_read = mplog_read;
233 wth->subtype_seek_read = mplog_seek_read;
234 wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_MPLOG;
236 /* skip the file header */
237 if (-1 == file_seek(wth->fh, 0x80, SEEK_SET, err))
238 return WTAP_OPEN_ERROR;
241 return WTAP_OPEN_MINE;
246 * Editor modelines - http://www.wireshark.org/tools/modelines.html
251 * indent-tabs-mode: nil
254 * vi: set shiftwidth=4 tabstop=8 expandtab:
255 * :indentSize=4:tabSize=8:noTabs=true: