オルタナティブ・ブログ > mtaneda ブログ >

中小企業の開発者は会社で何をしているのか

PHPでパケットキャプチャ

»

単なる実験ですが、PHPでパケットキャプチャするようなものを作ってみました。

元々PHPは、ウェブアプリケーション用の言語なのでOSに依存するリンクレイヤを扱うようなAPIは用意されていません。
無いなら作ってしまえ!ということで、Linux専用ですが extension を作りました。(実験なので超適当です)

Debian 5 での例
# su
# apt-get source php5
# exit
% cd php5-5.2.6.dfsg.1/ext
% ./ext_skel --extname=エクステンション名    エクステンション名には好きな名前をつけます
% cd エクステンション名
% vi config.m4

dnl PHP_ARG_ENABLE(エクステンション名, whether to enable エクステンション名 support,
dnl Make sure that the comment is aligned:
dnl [ --enable-エクステンション名 Enable エクステンション名 support])
この3行のdnlを消して保存する。

% phpize    (このコマンドが見つからないときは、apt-get install php5-dev をしておく。)
% ./configure
% vi php_エクステンション名.h

PHP_FUNCTION(confirm_エクステンション名_compiled); /* For testing, remove later. */
の下に、これから作る関数名を追加する。
PHP_FUNCTION(raw_socket);
PHP_FUNCTION(raw_close);
PHP_FUNCTION(raw_recv);

% vi エクステンション名.c

#include "php_エクステンション名.h"
の下に、必要なヘッダファイルを書いておく。
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <linux/if.h>

#include <arpa/inet.h>
#include <net/ethernet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <netinet/ip_icmp.h>
#include <netinet/if_ether.h>
#include <netinet/in.h>
#include <netpacket/packet.h>
#include <netdb.h>

#include <ctype.h>
#include <errno.h>
#include <ifaddrs.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
(とりあえず、自分の著書からコピーしたのでいらないのも含まれています・・・)

zend_function_entry エクステンション名_functions[] = {
  PHP_FE(confirm_エクステンション名_compiled, NULL) /* For testing, remove later. */
と、
  {NULL, NULL, NULL} /* Must be the last line in エクステンション名_functions[] */
}
の間に、これから作る関数名を追加しておきます。
  PHP_FE(raw_socket, NULL)
  PHP_FE(raw_close, NULL)
  PHP_FE(raw_recv, NULL)

ファイルの一番末尾に関数の実態を書きます。

PHP_FUNCTION(raw_socket)
{
struct ifreq if_req;
struct sockaddr_ll sa;
int soc;
char *str;
int str_len;

  if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
               "s",
               &str,
               &str_len)==FAILURE){
    return;
  }
  /* ソケットの生成 */
  if ((soc = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) == -1) {
    perror("socket");
    return;
  }
  /* インタフェース情報の取得 */
  (void) snprintf(if_req.ifr_name, sizeof(if_req.ifr_name), "%s", str);
  if (ioctl(soc, SIOCGIFINDEX, &if_req) == -1) {
    perror("ioctl");
    (void) close(soc);
    return;
  }
  /* インタフェースをbind() */
  sa.sll_family = PF_PACKET;
  sa.sll_protocol = htons(ETH_P_ALL);
  sa.sll_ifindex = if_req.ifr_ifindex;
  if (bind(soc, (struct sockaddr *) &sa, sizeof(sa)) == -1) {
    perror("bind");
    (void) close(soc);
    return;
  }

  /* インタフェースのフラグ取得 */
  if (ioctl(soc, SIOCGIFFLAGS, &if_req) == -1) {
    perror("ioctl");
    (void) close(soc);
    return;
  }
  /* インタフェースの*/
  if_req.ifr_flags = if_req.ifr_flags|IFF_PROMISC|IFF_UP;
  if (ioctl(soc, SIOCSIFFLAGS, &if_req) == -1) {
    perror("ioctl");
    (void) close(soc);
    return (-1);
  }
  RETURN_LONG(soc);
}

PHP_FUNCTION(raw_close)
{
long l;
  if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
               "l",
               &l)==FAILURE){
    return;
  }
  RETURN_LONG(close(l));
}

PHP_FUNCTION(raw_recv)
{
long l,len,i;

char buf[1500];
  if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
               "l",
               &l)==FAILURE){
    return;
  }
  array_init(return_value);
  len=recv(l,buf,1500,0);
  for(i=0;i<len;i++){
    add_index_long(return_value,i,buf[i]);
  }
}

ここまで来たら、エクステンションをビルドします。

% make
# su
# cp modules/エクステンション名.so /usr/lib/php5/20060613+lfs
# vi pdump.php

<?php
    dl('エクステンション名.so');
    $soc=raw_socket("eth0");
    $buf=raw_recv($soc);
    foreach($buf as $c){
        printf("%x",$c);
    }
    echo "\n";
    raw_close($soc);
?>

# php ./pdump.php

エクステンション名 => enabled
01b21e17ffffff840ffffffa0ffffffde34ffffffb9228045100345affffffb64003f65effffff97ffffffc0ffffffa80ffffffdcffffffc0ffffffa803affffffc7e016ffffffc36effffffb3ffffffa0affffffe1957ffffff8010ffffffffffffffffffffffd0ffffffea00118a1134ffffffb814d7ffffffffa37

無事、1個のパケットが出力されました!

以上、本日の実験でした。

https://www.amazon.co.jp/dp/4798028622

Comment(0)