Change Timestamp In PCAP File With C

A while back I needed to update a pcap file with about 20,000 packets in it. Each packet needed to have its timestamp essentially one millisecond after the other. My initial thought was to just get a hex editor and modify the packet, until I realized there were 20,000 packets in this pcap file, and the pcap was over 60 megabytes of packet data. Luckily I found the pcap API that provide a set of functions to modify the pcap in an offline mode. During compilation you simply have to refer to the library which allows this modification of pcap’s in “offline” mode. What happens behind the scenes is the reading of the pcap file using some wrapper function that simply does a file open and read based on certain offsets, which happens to work great!

Thanks to some great wiki articles and references that I used:

With these articles I was able to piece together a solution to modifying the time stamps and re-build a valid PCAP file viewable in Wireshark.

Its not my best work but it did the trick, and hopefully can help anyone in the future who needs to modify a PCAP file.

#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <syslog.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <net/ethernet.h>
#include <netinet/ether.h>
#include <net/if.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <netinet/tcp.h>
 
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
 
#include "pcap.h"
 
#if 0 
// For easy reference
typedef struct pcap_packet_hdr_s { 
	u_int32_t ts_sec;         /* timestamp seconds */
	u_int32_t ts_usec;        /* timestamp microseconds */
	u_int32_t capt_len;       /* number of octets of packet saved in file */
	u_int32_t orig_len;       /* actual length of packet */
} __attribute__((packed)) pcap_packet_hdr_t;
 
struct pcap_packet_s 
{
	pcap_packet_hdr_t hdr;
	u_int8_t data[0];
}__attribute__((packed)); 
typedef struct pcap_packet_s pcap_packet_t;
#endif 
 
int main(int argc, char *argv[])
{
    char ErrBuff [1024];
    int PacketCount, len;
    const u_char *PacketData;
    struct pcap_pkthdr *header;
 
    if(argc < 3){
        fprintf(stderr, "usage: ./pcap_parse in.pcap out.pcap\n");
        return -1;
    }
 
// ----- Read the header of the PCAP file
    int fp_for_header = open(argv[1],O_RDONLY);
    if(fp_for_header < 0){
        fprintf(stderr, "Cannot open PCAP file to get file header.\n");
        return -1;
    }
 
    u_int8_t data[sizeof(struct pcap_file_header)];
 
    len = read(fp_for_header, data, sizeof(struct pcap_file_header));
    if(len == 0){
        fprintf(stderr, "Could not read from file.\n");
    }	
    close(fp_for_header);
// ------- End read of header.
 
// ------- Open file for reading each packet
    int fp = open(argv[2],O_CREAT|O_WRONLY,S_IRWXU);
    if(fp < 0){
        fprintf(stderr, "Cannot open file: %s for writing.\n",argv[2]);
        return -1;
    }		
 
// Write header to new file
    len = write(fp, data,sizeof(struct pcap_file_header));
 
    pcap_t *pcap = pcap_open_offline(argv[1], ErrBuff);
    if (!pcap){
        fprintf (stderr, "Cannot open PCAP file '%s'\n",
                argv[1]);
                fprintf(stderr, "%s\n",ErrBuff);
                return -1;
    }
 
// Modify each packet's timestamp to be immediately after each other
    for (PacketCount = 0; pcap_next_ex (pcap, &header, &PacketData) > 0; PacketCount++){
        header->ts.tv_sec = 0;
        header->ts.tv_usec = PacketCount;
 
        len = write(fp,header,16);
        if(len == 0){
            fprintf(stderr, "Error occurred writing pcap_header.\n");
            pcap_close(pcap);
            close(fp);
            return -1;
        }
 
        len = write(fp,PacketData,header->caplen);
        if(len == 0){
            fprintf(stderr, "Error occurred writing pcap data.\n");
            pcap_close(pcap);
            close(fp);
            return -1;
        }	
    }
 
    pcap_close (pcap);
    close(fp);
    return PacketCount;
}

The premise of this program is, read in the header of the entire PCAP file that contains a ‘magic number’ which is used to tell the validity of the file within Wireshark, copy this to the head of the output file. Then read passed the header and use the pcap_open_offline and pcap_next_ex to read each packet individually and modify the timestamp of each packet as we go, then write the modified packet to the output file and voila.

This is just a simple example of modifying the timestamp(s), in theory you could modify the IP header of each packet and change the source and destination IP’s to suite a test network. Any value within the packet could be modified. Happy pcap modifying!