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 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 The mplog file format logs the communication between a contactless
28 smartcard and a card reader. Such files contain information about the
29 physical layer as well as the bytes exchanged between devices.
30 Some commercial logging and testing tools by the French company Micropross
33 The information used for implementing this wiretap module were
34 obtained from reverse-engineering. There is no publicly available
35 documentation of the mplog file format.
37 Mplog files start with the string "MPCSII". This string is part of
38 the header which is in total 0x80 bytes long.
40 Following the header, the file is a sequence of 8 byte-blocks.
45 The timestamp is a counter in little-endian format. The counter is in
53 #include <file_wrappers.h>
58 #define TYPE_PCD_PICC_A 0x70
59 #define TYPE_PICC_PCD_A 0x71
60 #define TYPE_PCD_PICC_B 0x72
61 #define TYPE_PICC_PCD_B 0x73
62 #define TYPE_UNKNOWN 0xFF
64 #define KNOWN_TYPE(x) \
66 ((x) == TYPE_PCD_PICC_A) || \
67 ((x) == TYPE_PICC_PCD_A) || \
68 ((x) == TYPE_PCD_PICC_B) || \
69 ((x) == TYPE_PICC_PCD_B) \
72 #define MPLOG_BLOCK_SIZE 8
74 /* ISO14443 pseudo-header, see http://www.kaiser.cx/pcap-iso14443.html */
75 #define ISO14443_PSEUDO_HDR_VER 0
76 #define ISO14443_PSEUDO_HDR_LEN 4
77 /* the two transfer events are the types that include a trailing CRC
78 the CRC is always present in mplog files */
79 #define ISO14443_PSEUDO_HDR_PICC_TO_PCD 0xFF
80 #define ISO14443_PSEUDO_HDR_PCD_TO_PICC 0xFE
83 #define ISO14443_MAX_PKT_LEN 256
85 #define PKT_BUF_LEN (ISO14443_PSEUDO_HDR_LEN + ISO14443_MAX_PKT_LEN)
88 /* read the next packet, starting at the current position of fh
89 as we know very little about the file format, our approach is rather simple:
90 - we read block-by-block until a known block-type is found
91 - this block's type is the type of the next packet
92 - this block's timestamp will become the packet's timestamp
93 - the data byte will be our packet's first byte
94 - we carry on reading blocks and add the data bytes
95 of all blocks of "our" type
96 - if a different well-known block type is found, this is the end of
97 our packet, we go back one block so that this block can be picked
98 up as the start of the next packet
99 - if two blocks of our packet's block type are more than 200us apart,
100 we treat this as a packet boundary as described above
102 static gboolean mplog_read_packet(FILE_T fh, struct wtap_pkthdr *phdr,
103 Buffer *buf, int *err, gchar **err_info)
106 /* --- the last block of a known type --- */
107 guint64 last_ctr = 0;
108 /* --- the current block --- */
109 guint8 block[MPLOG_BLOCK_SIZE]; /* the entire block */
110 guint8 data, type; /* its data and block type bytes */
111 guint64 ctr; /* its timestamp counter */
112 /* --- the packet we're assembling --- */
114 guint8 pkt_type = TYPE_UNKNOWN;
115 /* the timestamp of the packet's first block,
116 this will become the packet's timestamp */
120 ws_buffer_assure_space(buf, PKT_BUF_LEN);
121 p = ws_buffer_start_ptr(buf);
124 /* leave space for the iso14443 pseudo header
125 we can't create it until we've seen the entire packet */
126 p += ISO14443_PSEUDO_HDR_LEN;
129 if (!wtap_read_bytes_or_eof(fh, block, sizeof(block), err, err_info)) {
130 /* If we've already read some data, if this failed with an EOF,
131 so that *err is 0, it's a short read. */
132 if (pkt_bytes != 0) {
134 *err = WTAP_ERR_SHORT_READ;
140 ctr = pletoh48(&block[2]);
142 if (pkt_type == TYPE_UNKNOWN) {
143 if (KNOWN_TYPE(type)) {
149 if (type == pkt_type) {
151 /* if the distance to the last byte of the
152 same type is larger than 200us, this is very likely the
153 first byte of a new packet -> go back one block and exit
154 ctr and last_ctr are in units of 10ns
155 at 106kbit/s, it takes approx 75us to send one byte */
156 if (ctr - last_ctr > 200*100) {
157 file_seek(fh, -MPLOG_BLOCK_SIZE, SEEK_CUR, err);
166 else if (KNOWN_TYPE(type)) {
167 file_seek(fh, -MPLOG_BLOCK_SIZE, SEEK_CUR, err);
170 } while (pkt_bytes < ISO14443_MAX_PKT_LEN);
172 if (pkt_type == TYPE_UNKNOWN)
175 start_p[0] = ISO14443_PSEUDO_HDR_VER;
177 if (pkt_type==TYPE_PCD_PICC_A || pkt_type==TYPE_PCD_PICC_B)
178 start_p[1] = ISO14443_PSEUDO_HDR_PCD_TO_PICC;
180 start_p[1] = ISO14443_PSEUDO_HDR_PICC_TO_PCD;
182 start_p[2] = pkt_bytes >> 8;
183 start_p[3] = pkt_bytes & 0xFF;
185 phdr->rec_type = REC_TYPE_PACKET;
186 phdr->pkt_encap = WTAP_ENCAP_ISO14443;
187 phdr->presence_flags = WTAP_HAS_TS | WTAP_HAS_CAP_LEN;
188 phdr->ts.secs = (time_t)((pkt_ctr*10)/(1000*1000*1000));
189 phdr->ts.nsecs = (int)((pkt_ctr*10)%(1000*1000*1000));
190 phdr->caplen = ISO14443_PSEUDO_HDR_LEN + pkt_bytes;
191 phdr->len = phdr->caplen;
198 mplog_read(wtap *wth, int *err, gchar **err_info, gint64 *data_offset)
200 *data_offset = file_tell(wth->fh);
202 return mplog_read_packet(
203 wth->fh, &wth->phdr, wth->frame_buffer, err, err_info);
208 mplog_seek_read(wtap *wth, gint64 seek_off, struct wtap_pkthdr *pkthdr,
209 Buffer *buf, int *err, gchar **err_info)
211 if (-1 == file_seek(wth->random_fh, seek_off, SEEK_SET, err))
214 if (!mplog_read_packet(wth->random_fh, pkthdr, buf, err, err_info)) {
215 /* Even if we got an immediate EOF, that's an error. */
217 *err = WTAP_ERR_SHORT_READ;
224 wtap_open_return_val mplog_open(wtap *wth, int *err, gchar **err_info)
229 ok = wtap_read_bytes(wth->fh, magic, 6, err, err_info);
231 if (*err != WTAP_ERR_SHORT_READ)
232 return WTAP_OPEN_ERROR;
233 return WTAP_OPEN_NOT_MINE;
235 if (memcmp(magic, "MPCSII", 6) != 0)
236 return WTAP_OPEN_NOT_MINE;
238 wth->file_encap = WTAP_ENCAP_ISO14443;
239 wth->snapshot_length = 0;
240 wth->file_tsprec = WTAP_TSPREC_NSEC;
244 wth->subtype_read = mplog_read;
245 wth->subtype_seek_read = mplog_seek_read;
246 wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_MPLOG;
248 /* skip the file header */
249 if (-1 == file_seek(wth->fh, 0x80, SEEK_SET, err))
250 return WTAP_OPEN_ERROR;
253 return WTAP_OPEN_MINE;
258 * Editor modelines - http://www.wireshark.org/tools/modelines.html
263 * indent-tabs-mode: nil
266 * vi: set shiftwidth=4 tabstop=8 expandtab:
267 * :indentSize=4:tabSize=8:noTabs=true: