Man page of CHIKU_WAIT(2)

システムソフトウェアとポエムの差が激しいので,高山病に注意

ソケットプログラミングの勉強も兼ねてCでポートスキャンツールを作ってみた

はじめに

普段C言語使っているのに、C言語であまりソケットプログラミングをしたことがなかったので勉強も兼ねて作ってみました。

github.com

ポートスキャンとは

ポートスキャンとは、ポートと呼ばれるTCP/UDPが用いる0から65535番までの仮想的な情報通信のための送受信のためのすべて扉に対し接続を試みることで、どのポートが利用可能かを調べる行為のことを指します。よくサイバー攻撃の前段階として行われたりします。*1 例えば、80番のポートに接続できた場合、そのサーバはHTTPサービスを稼働していることがわかります。

作り方

ソケットの接続のための準備

まず、接続のためにソケットと呼ばれるサーバとクライアントを接続する電話線のようなものを作る必要があります。そして接続するためにgoogle.comのようなホスト名をIPアドレスに変換して、sockaddr_inと呼ばれるポート番号やIPアドレスを設定する構造体にセットしてあげる必要があります。ここではそのための準備をします。

int set_addr(struct sockaddr_in *dstAddr, char **addr) {                                                                                                                                                
   struct hostent *host;                                                                                                                                                                                 
   char *IPBuffer;
   if (*(addr + 1) == '\0') {
     fprintf(stderr, "Error:Please specify the address as an argument\n");                                                                                                                               
     return 0;                                                                                                                                                                                           
  }                                                                                                                                                                                                     
  dstAddr->sin_family = AF_INET;                                                                                                                                                                                                                                                                                                                                                                                
  if (isdigit(*(*(addr + 1)))) {                                                                                                                                                                        
    dstAddr->sin_addr.s_addr = inet_addr(*(addr + 1));                                                                                                                                                  
  } else if ((host = gethostbyname(*(addr + 1))) != 0) {                                                                                                                                                
    IPBuffer = inet_ntoa(*((struct in_addr *)host->h_addr_list[0]));                                                                                                                                    
    printf("Host resolved to IP: %s\n", IPBuffer);                                                                                                                                                      
    dstAddr->sin_addr.s_addr = inet_addr(IPBuffer);                                                                                                                                                     
  } else {                                                                                                                                                                                              
    herror(*(addr + 1));                                                                                                                                                                                
    return 0;                                                                                                                                                                                           
  }                                                                                                                                                                                                     
  return 1;                                                                                                                                                                                             
}

dstAddrのメンバsin_familyにはアドレスファミリをセットします。ここではAF_INETをセットしてIPであることを指定します。次に、ホスト名をIPアドレスに変換するためにgethostbyname関数を使用し、ホスト名に対応する構造体 hostent を返します。この構造体hstentはホスト名やアドレスの長さを格納します。IPアドレス情報はh_addr_listに格納されているため、inet_ntoa、inet_addr関数を用いて構造体sockaddr_inの中で参照されている構造体in_addrに変換したIPアドレスをセットします。

ソケットの作成と接続

次にソケットの作成と接続を行います。

int connect_port(struct sockaddr_in *dstAddr, int port) {
  int dstSock = socket(AF_INET, SOCK_STREAM, 0);
  dstAddr->sin_port = htons(port);

  if (dstSock < 0) {
    fprintf(stderr, "Eroor:Fail to create Socket\n");
    return 0;
  }
  if (connect(dstSock, (struct sockaddr *)dstAddr, sizeof(*dstAddr)) != -1) {
    close(dstSock);
    return 1;
  }
  return 0;
}

まず、socket関数を用いてソケットの作成を行います。AF_INET、SOCK_STREAMの2つを引数を渡すことで、TCP/IPとして通信することを指定します。次に、dstSockのメンバsin_portにhtonsで整数からネットワーク・バイト・オーダに変換してセットします。そしてconnect関数を用いて接続します。dstSockが参照しているソケットをdstAddrで指定したアドレスに接続しようと試み、接続できるか否かを確認しています。

動作例

$ ./scanner localhost   
Host resolved to IP: 127.0.0.1
Starting the portscan 
80 : Open
631 : Open
44888 : Open

おわりに

Cで久しぶりにソケットを使ったプログラミングをしたので、やり方を忘れて思い出すまでに結構時間がかかったりしました。ポートスキャンはソケットを使う練習にはなると思うし、割と大変なわけでもないのでちょっと勉強がてらに作ってみるのも良いのかなって思いました。ただ、ポートスキャンはあまりよろしいものではなりませんので、あくまでも自分のホスト宛ぐらいの使用に留めましょう!

参考

*1 攻撃はポートスキャンから始まる(上) | 日経 xTECH(クロステック)