├── README.md ├── example-old1.php ├── example-old2.php ├── lib └── Monotek │ └── PCAP │ ├── Encoding.php │ ├── File │ ├── Reader.php │ └── Writer.php │ ├── Header.php │ └── Packet.php └── pcap.php /README.md: -------------------------------------------------------------------------------- 1 | php-pcap 2 | ======== 3 | A library to read pcap files. 4 | 5 | This is a small class I wrote when I needed to parse pcap files, the idea is to add more structured protocol parsing classes and a way to easily add new "disectors". 6 | 7 | Contents 8 | -------- 9 | 10 | * pcap.php - The original implementation (pcap file reader and writer, some basic protocol parsers). 11 | * example-old*.php - Examples against the original implementation. 12 | * lib/ - New implementation (incomplete, only pcap reader/writer, no protocol parsers yet). 13 | -------------------------------------------------------------------------------- /example-old1.php: -------------------------------------------------------------------------------- 1 | open($argv[1]); 13 | 14 | while ($s = $p->read_packet()) 15 | { 16 | $eth = parse_ethframe($s['data']); 17 | $ip = parse_ip($eth['data']); 18 | echo date("H:i:s", $s['ts_sec']).".".$s['ts_usec']." ".$ip['source_ip']." > ".$ip['destination_ip']."\n"; 19 | } 20 | -------------------------------------------------------------------------------- /example-old2.php: -------------------------------------------------------------------------------- 1 | open($argv[1]); 17 | 18 | $reg = array(); 19 | $num = array(); 20 | 21 | while ($s = $p->read_packet()) 22 | { 23 | $eth = parse_ethframe($s['data']); 24 | $ip = parse_ip($eth['data']); 25 | if ($ip['protocol'] == 6) { 26 | $tcp = parse_tcp($ip['data']); 27 | $data = $tcp['data']; 28 | $line = date("H:i:s", $s['ts_sec']).".".$s['ts_usec']." ".$ip['source_ip'].":".$tcp['source_port']." > ".$ip['destination_ip'].":".$tcp['destination_port']." TCP"; 29 | } else if ($ip['protocol'] == 17) { 30 | $udp = parse_udp($ip['data']); 31 | $data = $udp['data']; 32 | $line = date("H:i:s", $s['ts_sec']).".".$s['ts_usec']." ".$ip['source_ip'].":".$udp['source_port']." > ".$ip['destination_ip'].":".$udp['destination_port']." UDP"; 33 | } else { 34 | continue; 35 | } 36 | 37 | if ($filter !== false) { 38 | if (strpos($data,$filter) === false) continue; 39 | } 40 | 41 | echo $line."\n"; 42 | if ($filter !== false) { 43 | var_dump($data); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/Monotek/PCAP/Encoding.php: -------------------------------------------------------------------------------- 1 | handle = @fopen($file, "r"); 14 | if ($this->handle === false) { 15 | throw new \Exception("Opening '$file' for reading failed, does the file exist?"); 16 | } 17 | $this->header = $this->getHeader(); 18 | return $this->header; 19 | } 20 | 21 | private function getHeader() 22 | { 23 | $buffer = fread($this->handle, 4); 24 | $row = unpack("Vmagic", $buffer); 25 | $this->magic = $row['magic']; 26 | 27 | $buffer = fread($this->handle, 20); 28 | 29 | $header = new \Monotek\PCAP\Header; 30 | $header->decode($this->magic, $buffer); 31 | 32 | return $header; 33 | } 34 | 35 | public function getPacket() 36 | { 37 | if (feof($this->handle)) { 38 | return false; 39 | } 40 | $buffer = fread($this->handle, 16); 41 | 42 | $packet = new \Monotek\PCAP\Packet; 43 | $packet->decode($this->magic, $buffer); 44 | 45 | if (($packet->incl_len > min($packet->orig_len, $this->header->snaplen)) || ($packet->incl_len == 0)) { 46 | throw new \Exception("Bad packet header (incl_len = ".$packet->incl_len.")"); 47 | } 48 | 49 | $packet->setData(fread($this->handle, $head['incl_len'])); 50 | return $packet; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /lib/Monotek/PCAP/File/Writer.php: -------------------------------------------------------------------------------- 1 | handle = @fopen($file, "w"); 13 | if ($this->handle === false) { 14 | throw new \Exception("Opening '$file' failed, is the dir writable? Disk full?"); 15 | } 16 | } 17 | 18 | public function writeHeader(\Monotek\PCAP\Header $header) 19 | { 20 | fwrite($this->handle, $header->encode($this->magic)); 21 | } 22 | 23 | public function writePacket(\Monotek\PCAP\Packet $packet) 24 | { 25 | fwrite($this->handle, $packet->encode($this->magic)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/Monotek/PCAP/Header.php: -------------------------------------------------------------------------------- 1 | header)) { 12 | throw new \Exception("Can't find field '$var' in header data."); 13 | } 14 | return $this->header[$var]; 15 | } 16 | 17 | function decode($magic, $buffer) 18 | { 19 | $encoding = $this->getEncoding($magic); 20 | 21 | $this->header = unpack( 22 | $encoding->u16 . "version_major/". 23 | $encoding->u16 . "version_minor/". 24 | $encoding->u32 . "thiszone/". 25 | $encoding->u32 . "sigfigs/". 26 | $encoding->u32 . "snaplen/". 27 | $encoding->u32 . "network", 28 | $buffer); 29 | } 30 | 31 | function encode($magic) 32 | { 33 | $encoding = $this->getEncoding($magic); 34 | 35 | $retval = pack($encoding->u32, hexdec($magic))); 36 | 37 | $retval .= pack($encoding->u16, $this->version_major); 38 | $retval .= pack($encoding->u16, $this->version_minor); 39 | $retval .= pack($encoding->u32, $this->thiszone); 40 | $retval .= pack($encoding->u32, $this->sigfigs); 41 | $retval .= pack($encoding->u32, $this->snaplen); 42 | $retval .= pack($encoding->u32, $this->network); 43 | 44 | return $retval; 45 | } 46 | } -------------------------------------------------------------------------------- /lib/Monotek/PCAP/Packet.php: -------------------------------------------------------------------------------- 1 | packet)) { 12 | throw new \Exception("Can't find field '$var' in packet data."); 13 | } 14 | return $this->packet[$var]; 15 | } 16 | 17 | function decode($magic, $buffer) 18 | { 19 | $encoding = $this->getEncoding($magic); 20 | 21 | $this->packet = unpack( 22 | $encoding->u32 . "ts_sec/". 23 | $encoding->u32 . "ts_usec/". 24 | $encoding->u32 . "incl_len/". 25 | $encoding->u32 . "orig_len", 26 | $buffer); 27 | } 28 | 29 | function setData($data) 30 | { 31 | $this->packet['data'] = $data; 32 | } 33 | 34 | function encode($magic) 35 | { 36 | $encoding = $this->getEncoding($magic); 37 | 38 | $retval = pack($encoding->u32, hexdec($magic))); 39 | 40 | $retval .= pack($encoding->u32, $this->ts_sec); 41 | $retval .= pack($encoding->u32, $this->ts_usec); 42 | $retval .= pack($encoding->u32, $this->incl_len); 43 | $retval .= pack($encoding->u32, $this->orig_len); 44 | $retval .= $this->data; 45 | 46 | return $retval; 47 | } 48 | } -------------------------------------------------------------------------------- /pcap.php: -------------------------------------------------------------------------------- 1 | f = fopen($file, "r"); 16 | $r = $this->read_global_header(); 17 | $this->count = 1; 18 | return $r; 19 | } 20 | 21 | function close() 22 | { 23 | fclose($this->f); 24 | } 25 | 26 | private function read_global_header() 27 | { 28 | $buf = fread($this->f, 4); 29 | $x = unpack($this->u32."magic", $buf); 30 | if (sprintf("%x",$x['magic']) == "a1b2c3d4") { 31 | $this->u32 = "V"; 32 | $this->u16 = "v"; 33 | } else 34 | if (sprintf("%x",$x['magic']) == "d4c3b2a1") { 35 | $this->u32 = "N"; 36 | $this->u16 = "n"; 37 | } else { 38 | throw new Exception("Unknown file format"); 39 | } 40 | $buf = fread($this->f, 20); 41 | $this->global_header = unpack($this->u16."version_major/". 42 | $this->u16."version_minor/". 43 | $this->u32."thiszone/". 44 | $this->u32."sigfigs/". 45 | $this->u32."snaplen/". 46 | $this->u32."network", 47 | $buf); 48 | return $this->global_header; 49 | } 50 | 51 | public function read_packet() 52 | { 53 | $buf = fread($this->f, 16); 54 | if (feof($this->f)) return false; 55 | $head = unpack($this->u32."ts_sec/". 56 | $this->u32."ts_usec/". 57 | $this->u32."incl_len/". 58 | $this->u32."orig_len/", 59 | $buf); 60 | if ($head['incl_len'] > $head['orig_len'] || $head['incl_len'] > $this->global_header['snaplen']) { 61 | throw new Exception("Bad packet header (incl_len)"); 62 | } 63 | if ($head['incl_len'] == 0) { 64 | var_dump($buf); 65 | var_dump($head);die("0???\n"); 66 | } 67 | $head['count'] = $this->count++; 68 | $head['data'] = fread($this->f, $head['incl_len']); 69 | return $head; 70 | } 71 | 72 | } 73 | 74 | class pcap_file_writer 75 | { 76 | 77 | private $f; 78 | private $u32 = "V"; // L ? 79 | private $u16 = "v"; // S ? 80 | private $global_header; 81 | 82 | function open($file) 83 | { 84 | $this->f = fopen($file, "w"); 85 | } 86 | 87 | public function write_global_header($head) 88 | { 89 | fwrite($this->f, pack($this->u32, 0xa1b2c3d4)); 90 | fwrite($this->f, pack($this->u16.$this->u16.$this->u32.$this->u32.$this->u32.$this->u32, 91 | $head['version_major'], 92 | $head['version_minor'], 93 | $head['thiszone'], 94 | $head['sigfigs'], 95 | $head['snaplen'], 96 | $head['network'])); 97 | } 98 | 99 | public function write_packet($head) 100 | { 101 | fwrite($this->f, pack($this->u32.$this->u32.$this->u32.$this->u32, 102 | $head['ts_sec'], 103 | $head['ts_usec'], 104 | $head['incl_len'], 105 | $head['orig_len'])); 106 | fwrite($this->f, $head['data']); //$data 107 | } 108 | 109 | } 110 | 111 | 112 | 113 | function parse_sip($data) 114 | { 115 | $lines = explode("\r\n", $data); 116 | $ret['command'] = $lines[0]; 117 | unset($lines[0]); 118 | foreach ($lines as $line) { 119 | if ($line == "") break; // dont care about data 120 | list($k,$v) = explode(": ", $line, 2); 121 | $ret[$k] = $v; 122 | } 123 | list(,$ret['data']) = explode("\r\n\r\n", $data, 2); 124 | return $ret; 125 | } 126 | 127 | function cut_ip($data) 128 | { 129 | return substr($data,14); 130 | } 131 | 132 | function parse_ethframe($data) 133 | { 134 | $x = unpack("nethertype", substr($data,12,2)); 135 | $x['destination_mac'] = bin2hex(substr($data,0,6)); 136 | $x['source_mac'] = bin2hex(substr($data,6,6)); 137 | $x['data'] = substr($data,14); 138 | return $x; 139 | } 140 | 141 | function parse_ip($data) 142 | { 143 | $x = unpack("Cversion_ihl/Cservices/nlength/nidentification/nflags_offset/Cttl/Cprotocol/nchecksum/Nsource/Ndestination", $data); 144 | $x['version'] = $x['version_ihl'] >> 4; 145 | $x['ihl'] = $x['version_ihl'] & 0xf; 146 | unset($x['version_ihl']); 147 | $x['flags'] = $x['flags_offset'] >> 13; 148 | $x['offset'] = $x['flags_offset'] & 0x1fff; 149 | $x['source_ip'] = long2ip($x['source']); 150 | $x['destination_ip'] = long2ip($x['destination']); 151 | $x['data'] = substr($data,$x['ihl']*4,$x['length']-$x['ihl']*4); // ignoring options 152 | return $x; 153 | } 154 | 155 | function parse_udp($data) 156 | { 157 | $x = unpack("nsource_port/ndestination_port/nlength/nchecksum",$data); 158 | $x['data'] = substr($data,8,$x['length']-8); 159 | return $x; 160 | } 161 | 162 | function get_media_port($dict) 163 | { 164 | if (!isset($dict['Content-Type'])) return false; 165 | if ($dict['Content-Type'] != "application/sdp") return false; 166 | 167 | if (preg_match("/m=audio ([0-9]+) /s", $dict['data'], $match)) { 168 | return intval($match[1]); 169 | } 170 | return false; 171 | } 172 | 173 | 174 | function parse_tcp($data) 175 | { 176 | $x = unpack("nsource_port/ndestination_port/Nseq/Nack/Ctmp1/Ctmp2/nwindow/nchecksum/nurgent", $data); 177 | $x['offset'] = ($x['tmp1']>>4)&0xf; 178 | $x['flag_NS'] = ($x['tmp1']&0x01) != 0; 179 | $x['flag_CWR'] = ($x['tmp2']&0x80) != 0; 180 | $x['flag_ECE'] = ($x['tmp2']&0x40) != 0; 181 | $x['flag_URG'] = ($x['tmp2']&0x20) != 0; 182 | $x['flag_ACK'] = ($x['tmp2']&0x10) != 0; 183 | $x['flag_PSH'] = ($x['tmp2']&0x08) != 0; 184 | $x['flag_RST'] = ($x['tmp2']&0x04) != 0; 185 | $x['flag_SYN'] = ($x['tmp2']&0x02) != 0; 186 | $x['flag_FIN'] = ($x['tmp2']&0x01) != 0; 187 | unset($x['tmp1']); 188 | unset($x['tmp2']); 189 | $x['data'] = substr($data, 4*$x['offset']); 190 | return $x; 191 | } 192 | 193 | 194 | 195 | --------------------------------------------------------------------------------