翻譯作品之四Coding a TCP Connect Port Scanner:Using Input Files rev1.0

翻譯作品之四Coding a TCP Connect Port Scanner:Using Input Files rev1.0
             --nightcat

這篇文章假設讀者已經讀了truncode.org的文章Coding a TCP Connect Port Scanner: Step by Step 或者已經有了TCP/IP套接字編程的基本技能。此文是建立在Coding a TCP Connect Port Scanner: Step by Step這篇文章之上。

]介紹

internet給系統管理員,網絡滲透試驗者,放火牆分析師,和黑客提供了幾乎無數的安全掃描器。試一下在象http://packetstormsecurity.com這樣的站點上搜索一下,不會化多長時間就有大量很好的c代碼淹沒你。

多數的小端口和漏洞掃描器,僅僅每次掃描一個目標。這裏你有兩個選擇。你既可以循環這個程序用shell腳本,也可以自己修改一個原代碼。

如果你對寫自己的程序不滿意,可以借鑑編寫完美的程序的其中的函數來充實自己的程序。在這篇文章我採取這種方法。

因爲這篇文章是input/output例子,我選擇一個從[email protected]所寫的完美的snmp暴力破解掃描器onesixtyone 分離出一個 read_hosts().在未來某時,修改和提取存在的你偶遇的代碼之後,你自然會開始開發自己的程序而不必思考它。你不是很厲害的時候,用存在的代碼可以給自己帶來信心。事實上這也是最好的學習的方法。要做的僅是理解用到的每行代碼。


]文件輸入函數:

讓我們先熟悉打開文件的方法

---------------------------------------------------------------------------
fopen()
---------------------------------------------------------------------------

爲了用一個文件,我們先需要打開它,這個可以用fopen()來完成。這個函數的原型如下:
FILE *fopen (const char *path, const char *mode);

這個fopen()函數有兩個參數:目標文件的路徑和打開模式,打開模式決定我們將會如何打開它。如果成功打開,將返回一個文件指針。


---------------------------------------------------------------------------
fclose()
---------------------------------------------------------------------------

這個函數正如你所想的:關閉一個文件。這個函數被期待傳遞給一個文件指針。

---------------------------------------------------------------------------
fgetc()
---------------------------------------------------------------------------

這個函數每次讀取一個字符。這是個神奇的函數允許我們從一個ip地址或者主機列表中讀取。例如:每次 fgetc() 返回,我們簡單的增加到下一個字符,直到到達文件結尾。

以下的簡單程序用先前的函數打開一個文件和打印出每行。

---------------------------------------------------------------------------
#include <stdio.h>

int main(int argc, char *argv[])
{
    FILE *fd;
    char buf[100];
    char ch, *iplist = argv[1];
    int c;

    if ((fd = fopen(iplist, "r")) == 0) {
        printf("Error opening input file %s/n", iplist);
        exit(1);
    }
    c = 0; ch = 0;

    while((ch = fgetc(fd)) != EOF) {
        if (ch == '/n' || ch == ' ' || ch == '/t') {
            buf[c] = '/0';
     if (c > 0) {
  printf("%s/n", buf);
     }
     c = 0;
        } else {
     buf[c++] = ch;
        }
        if (c > sizeof(buf)-1) {
            exit(1);
        }
    }
    fclose(fd);
    return 0;
}
---------------------------------------------------------------------------

首先,我們定義一個文件指針*fd.這個fopen() 函數到那時打開一個文件通過關聯到文件指針流。但我們用fgetc()循環每個字符,這個程序檢查新行,空格或者tab鍵,以表示輸入的結束。當遇到一個新行,空格,或者tab,就打印一個字符串,和再次開始循環。這個程序不一定令人感興趣。但是他適合一個開始寫小的端口或者漏洞掃描器。


讓我們修改這個程序通過用gethostbyaddr()程序來替換printf()聲明。現在這個程序僅僅小小的調整就變的更有用。他能快速在一個大的的ip列表中反向dns查找。

---------------------------------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main(int argc, char *argv[])
{
    struct hostent *he;
    struct in_addr t_addr;
    FILE *fd;
    char buf[100];
    char ch, *iplist = argv[1];
    int c;

    if ((fd = fopen(iplist, "r")) == 0) {
        printf("Error opening input file %s/n", iplist);
        exit(1);
    }
    c = 0; ch = 0;

    while((ch = fgetc(fd)) != EOF) {
        if (ch == '/n' || ch == ' ' || ch == '/t') {
            buf[c] = '/0';
            if (c > 0) {

  t_addr.s_addr = inet_addr(buf);

  if ((he=gethostbyaddr((char*)&(t_addr.s_addr),
      sizeof(buf),AF_INET)) == NULL) {
      herror("gethostbyname");
      exit(1);
         }
  printf("%s/n", he->h_name);
     }
     c = 0;
 } else {
     buf[c++] = ch;
        }
 if (c > sizeof(buf)-1) {
     exit(1);
        }
    }
    fclose(fd);
    return 0;
}
---------------------------------------------------------------------------

  一看之下,這個程序看起來比前面哪個複雜,但是邏輯上是一樣的。用這個簡單的模板,我們能開發很多有用的程序。這次我們能換出gethostbyaddr()程序用一個簡單的tcp連接端口掃描函數。這個程序被轉換成一個能夠掃描整個子網的端口掃描器。


---------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#define TRUE    1
#define FALSE   0
#define SSH     22

/*
 * simple TCP port scanner
 */
int portscan(char *remote_ip, u_short port)
{
    int sock_fd;
    int state;
    struct sockaddr_in target;

    sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    memset(&target, 0, sizeof(target));
    target.sin_family   = AF_INET;
    target.sin_addr.s_addr = inet_addr(remote_ip);
    target.sin_port = htons(port);

    if(connect(sock_fd,(struct sockaddr *)&target,sizeof(target))==0)
    {
        state = TRUE;
    } else {
 state = FALSE;
    }
    close(sock_fd);
    return state;
}

int main(int argc, char *argv[])
{
    struct hostent *he;
    struct in_addr t_addr;
    FILE *fd;
    char buf[100];
    char ch, *iplist = argv[1];
    int c, i;

    if ((fd = fopen(iplist, "r")) == 0) {
 printf("Error opening input file %s/n", iplist);
 exit(1);
    }

    while((ch = fgetc(fd)) != EOF) {
 if (ch == '/n' || ch == ' ' || ch == '/t') {
     buf[c] = '/0';
     if (c > 0) {
  if((i = portscan(buf, SSH) == 0))
      printf("%s has port %d closed./n", buf, SSH);
  else
      printf("%s has port %d open./n", buf, SSH);
     }
     c = 0;
 } else {
     buf[c++] = ch;
 }
 if (c > sizeof(buf)-1) {
     exit(1);
 }
    }
    fclose(fd);
    return 0;
}
---------------------------------------------------------------------------

這裏似乎對fopen(), fclose(), 和fgetc()是無限制。作爲一個練習,擇取一些你喜歡的只限制一個命令行只有一個端口的掃描器和添加進代碼成爲新的。
目前這個端口掃描器只掃描一個端口。沒關係,這裏我們需要做的是在portscan()周圍增加一個簡單的循環和一對新的變量,就象下面:

---------------------------------------------------------------------------
int start, end, counter;

start = atoi(argv[2]);
end = atoi(argv[3]);
---------------------------------------------------------------------------

增加一個簡單的循環在portscan()函數週圍:

---------------------------------------------------------------------------
if (c > 0) {
    for(counter = start; counter <= end; counter++)
    {
 if((i = portscan(buf, counter) == 0))
     printf("%s has port %d closed./n", buf, counter);
 else
     printf("%s has port %d open./n", buf, counter);
    }
}

---------------------------------------------------------------------------


運行輸出結果如下:

modular@truncode$ ./portscan hostips.txt 1 25
192.168.1.100 has port 1 closed.
192.168.1.100 has port 2 closed.
192.168.1.100 has port 3 closed.
192.168.1.100 has port 4 closed.
192.168.1.100 has port 5 closed.
...

這看起來相當難看,有意義的去修改這個程序,因而他輸出一個ip地址和一個主機名列表,開放的端口,和服務。以下的例子可能是用子進程來處理。除這點外,在最後還出示了每個新的修改後的構造:

---------------------------------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#define TRUE    1
#define FALSE   0

/*
 * simple TCP port scanner function
 */
int portscan(char *remote_ip, u_short port)
{
    int sock_fd;
    int state;
    struct sockaddr_in target;

    sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    memset(&target, 0, sizeof(target));
    target.sin_family   = AF_INET;
    target.sin_addr.s_addr = inet_addr(remote_ip);
    target.sin_port = htons(port);

    if(connect(sock_fd,(struct sockaddr *)&target,sizeof(target))==0)
    {
 state = TRUE;
    } else {
 state = FALSE;
    }
    close(sock_fd);
    return state;
}

int main(int argc, char *argv[])
{
    struct hostent *he;
    struct servent *srvc;
    struct in_addr t_addr;
    FILE *fd;
    char buf[100];
    char ch, *iplist = argv[1];
    int c, i;
    int start, end, counter;

    start = atoi(argv[2]);
    end = atoi(argv[3]);

    if ((fd = fopen(iplist, "r")) == 0) {
 printf("Error opening input file %s/n", iplist);
 exit(1);
    }
    c = 0; ch = 0;

    while((ch = fgetc(fd)) != EOF) {
 if (ch == '/n' || ch == ' ' || ch == '/t') {
     buf[c] = '/0';
     if (c > 0) {

  t_addr.s_addr = inet_addr(buf);

  if ((he=gethostbyaddr((char*)&(t_addr.s_addr),
      sizeof(buf),AF_INET)) == NULL) {
      herror("gethostbyname");
      exit(1);
  }
  
  printf("/n");
  printf("Interesting ports on %s (%s)/n/n", he->h_name, buf);
  printf("port/tstate/tservice/n");
  for(counter = start; counter <= end; counter++)
  {   
      if((i = portscan(buf, counter) == 0))
   continue;
      else
   srvc = getservbyport(htons(counter), "tcp");
   printf("%d/tcp/topen/t%s/n", counter,
    (srvc == NULL)?"unknown":srvc->s_name);
  }
     }
     c = 0;
 } else {
     buf[c++] = ch;
 }
 if (c > sizeof(buf)-1) {
     exit(1);
 }
    }
    fclose(fd);
    return 0;
}
   
---------

增加了三個結構定義:
struct hostent *he;
struct servent *srvc;
struct in_addr t_addr;

  這個程序目前的版本已經從先前的例子中合併了gethostbyaddr(),因此DNS hostnames 也能被打印。getservbyport() 函數也別增加以致每個端口可以與標準服務相匹配。查閱第一篇文章Coding a TCP Connect Port Scanner: Step by Step。如果你需要複習這些函數是如何實現的。

這個系列的下篇文章將會示範怎樣合併可變長度的子網掩碼(VLSM)到一個端口掃描器裏。


三。附原文:

Coding a TCP Connect Port Scanner: Using Input Files
rev 1.0
- C Hacker's Handbook Series #2 -
 _                         _    
| |_ ___ _ _ ___ ___ ___ _| |___
|  _|  _| | |   |  _| . | . | -_|
|_| |_| |___|_|_|___|___|___|___|

* truncode security development *
http://truncode.org
modular <[email protected]>

This paper assumes the reader has read truncode.org's paper,
Coding a TCP Connect Port Scanner: Step by Step or already has basic
TCP/IP socket programming skills. This paper is intended to build upon the paper, Coding a TCP Connect Port Scanner: Step by Step.

] Introduction

The Internet offers an almost endless amount of security scanners to the
System Administrator, Network Penetration Tester, Firewall Analyst, and Hacker.
Try a few searches on a web site like http://packetstormsecurity.com and it
won't take long until the massive volume of C code available overwhelms you.

A majority of the smaller port and vulnerability scanners for example, will
scan only one target at a time. This leaves you with two choices. You can
either loop the program in a shell script or modify the source code itself.

If you don't yet feel comfortable with writing your own routines, look at some
more of the robust scanners out there with the existing functionality you
would like to see in your own tools. This is the approach I take in this paper.
For this paper's file input/output example I chose to rip the read_hosts()
function from the excellent snmp brute-force scanner onesixtyone written by
[email protected]. After some time modifying and tweaking existing
code you come across, you'll naturally begin to develop your own routines
without even thinking about it. It is a common belief that if you use existing
code, you are not "elite". This is actually the best way to learn. Just make
sure you understand every line you use!

] File Input Function:

Let us first get reacquainted with the process of opening files.

-------------------------------------------------------------------------------
fopen()
-------------------------------------------------------------------------------

In order to use a file, we first need to open it. This is accomplished with
the fopen() function. This function's prototype is as follows:

FILE *fopen (const char *path, const char *mode);

The fopen() function takes two arguments: the path of the target file and the
mode, which determines how we will be opening it. If we are successful fopen()
returns a FILE pointer.

-------------------------------------------------------------------------------
fclose()
-------------------------------------------------------------------------------

This function does exactly what you think it does: closes the file. This
function expects to be passed a FILE pointer.

-------------------------------------------------------------------------------
fgetc()
-------------------------------------------------------------------------------

This function reads in one character at a time. This is the magic function
that will allow us to read in a list of IP addresses or hostnames, for example.
Each time fgetc() returns, we simply increment to the next character until we
reach the end of the line.

The following simple program open's a file and prints each line using the
previous functions:
-------------------------------------------------------------------------------
#include <stdio.h>

int main(int argc, char *argv[])
{
    FILE *fd;
    char buf[100];
    char ch, *iplist = argv[1];
    int c;

    if ((fd = fopen(iplist, "r")) == 0) {
        printf("Error opening input file %s/n", iplist);
        exit(1);
    }
    c = 0; ch = 0;

    while((ch = fgetc(fd)) != EOF) {
        if (ch == '/n' || ch == ' ' || ch == '/t') {
            buf[c] = '/0';
     if (c > 0) {
  printf("%s/n", buf);
     }
     c = 0;
        } else {
     buf[c++] = ch;
        }
        if (c > sizeof(buf)-1) {
            exit(1);
        }
    }
    fclose(fd);
    return 0;
}
-------------------------------------------------------------------------------

First, we define the FILE pointer *fd. The fopen() function then opens a file
by associating a stream with the FILE pointer. As we loop through each
character using fgetc(), the  program checks for a newline, space, or tab
indicating the end of an entry. When a new line, space, or tab is encountered,
a string is printed and the loop starts again. This program isn't exactly
interesting, but it serves as a basis from which to begin writing a small port
or vulnerability scanner.

Let's modify this program by replacing the printf() statement with a
gethostbyaddr() routine. Now this program has become useful with only a few
adjustments. It can now do speedy reverse DNS lookups on a large list of IP
addresses.

---------------------------------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main(int argc, char *argv[])
{
    struct hostent *he;
    struct in_addr t_addr;
    FILE *fd;
    char buf[100];
    char ch, *iplist = argv[1];
    int c;

    if ((fd = fopen(iplist, "r")) == 0) {
        printf("Error opening input file %s/n", iplist);
        exit(1);
    }
    c = 0; ch = 0;

    while((ch = fgetc(fd)) != EOF) {
        if (ch == '/n' || ch == ' ' || ch == '/t') {
            buf[c] = '/0';
            if (c > 0) {

  t_addr.s_addr = inet_addr(buf);

  if ((he=gethostbyaddr((char*)&(t_addr.s_addr),
      sizeof(buf),AF_INET)) == NULL) {
      herror("gethostbyname");
      exit(1);
         }
  printf("%s/n", he->h_name);
     }
     c = 0;
 } else {
     buf[c++] = ch;
        }
 if (c > sizeof(buf)-1) {
     exit(1);
        }
    }
    fclose(fd);
    return 0;
}
-------------------------------------------------------------------------------

At first glance, this program looks much more complex than its predecessor,
but the logic is in fact the same. By using this simple template, we can
develop many useful programs. This time we can swap out the gethostbyaddr()
routine with a simple TCP connect port scanning function. Suddenly this
program is transformed into a port scanner capable of scanning whole
subnets!

-------------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#define TRUE    1
#define FALSE   0
#define SSH     22

/*
 * simple TCP port scanner
 */
int portscan(char *remote_ip, u_short port)
{
    int sock_fd;
    int state;
    struct sockaddr_in target;

    sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    memset(&target, 0, sizeof(target));
    target.sin_family   = AF_INET;
    target.sin_addr.s_addr = inet_addr(remote_ip);
    target.sin_port = htons(port);

    if(connect(sock_fd,(struct sockaddr *)&target,sizeof(target))==0)
    {
        state = TRUE;
    } else {
 state = FALSE;
    }
    close(sock_fd);
    return state;
}

int main(int argc, char *argv[])
{
    struct hostent *he;
    struct in_addr t_addr;
    FILE *fd;
    char buf[100];
    char ch, *iplist = argv[1];
    int c, i;

    if ((fd = fopen(iplist, "r")) == 0) {
 printf("Error opening input file %s/n", iplist);
 exit(1);
    }

    while((ch = fgetc(fd)) != EOF) {
 if (ch == '/n' || ch == ' ' || ch == '/t') {
     buf[c] = '/0';
     if (c > 0) {
  if((i = portscan(buf, SSH) == 0))
      printf("%s has port %d closed./n", buf, SSH);
  else
      printf("%s has port %d open./n", buf, SSH);
     }
     c = 0;
 } else {
     buf[c++] = ch;
 }
 if (c > sizeof(buf)-1) {
     exit(1);
 }
    }
    fclose(fd);
    return 0;
}
-------------------------------------------------------------------------------

There seem to be unlimited possibilities with fopen(), fclose(), and fgetc().
As an exercise, pick some of your favorite scanners that are limited to only
one host on the command line and hack the code into something new!  

So far this port scanner only scans one port. No problem. At this point all we
need to do is add a simple for loop around the portscan() function and a
couple new variables like so:
-------------------------------------------------------------------------------
int start, end, counter;

start = atoi(argv[2]);
end = atoi(argv[3]);
-------------------------------------------------------------------------------
And a simple for loop around the portscan() function:

-------------------------------------------------------------------------------

if (c > 0) {
    for(counter = start; counter <= end; counter++)
    {
 if((i = portscan(buf, counter) == 0))
     printf("%s has port %d closed./n", buf, counter);
 else
     printf("%s has port %d open./n", buf, counter);
    }
}

-------------------------------------------------------------------------------
The output is superfluous and is due for some tweaking:

modular@truncode$ ./portscan hostips.txt 1 25
192.168.1.100 has port 1 closed.
192.168.1.100 has port 2 closed.
192.168.1.100 has port 3 closed.
192.168.1.100 has port 4 closed.
192.168.1.100 has port 5 closed.
...

That looks rather ugly. It makes sense to modify the program so it outputs
one IP address and a list of hostnames, open ports, and services. The
following example may be child's play, but the point is to show how each new modification builds on the last:
-------------------------------------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#define TRUE    1
#define FALSE   0

/*
 * simple TCP port scanner function
 */
int portscan(char *remote_ip, u_short port)
{
    int sock_fd;
    int state;
    struct sockaddr_in target;

    sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    memset(&target, 0, sizeof(target));
    target.sin_family   = AF_INET;
    target.sin_addr.s_addr = inet_addr(remote_ip);
    target.sin_port = htons(port);

    if(connect(sock_fd,(struct sockaddr *)&target,sizeof(target))==0)
    {
 state = TRUE;
    } else {
 state = FALSE;
    }
    close(sock_fd);
    return state;
}

int main(int argc, char *argv[])
{
    struct hostent *he;
    struct servent *srvc;
    struct in_addr t_addr;
    FILE *fd;
    char buf[100];
    char ch, *iplist = argv[1];
    int c, i;
    int start, end, counter;

    start = atoi(argv[2]);
    end = atoi(argv[3]);

    if ((fd = fopen(iplist, "r")) == 0) {
 printf("Error opening input file %s/n", iplist);
 exit(1);
    }
    c = 0; ch = 0;

    while((ch = fgetc(fd)) != EOF) {
 if (ch == '/n' || ch == ' ' || ch == '/t') {
     buf[c] = '/0';
     if (c > 0) {

  t_addr.s_addr = inet_addr(buf);

  if ((he=gethostbyaddr((char*)&(t_addr.s_addr),
      sizeof(buf),AF_INET)) == NULL) {
      herror("gethostbyname");
      exit(1);
  }
  
  printf("/n");
  printf("Interesting ports on %s (%s)/n/n", he->h_name, buf);
  printf("port/tstate/tservice/n");
  for(counter = start; counter <= end; counter++)
  {   
      if((i = portscan(buf, counter) == 0))
   continue;
      else
   srvc = getservbyport(htons(counter), "tcp");
   printf("%d/tcp/topen/t%s/n", counter,
    (srvc == NULL)?"unknown":srvc->s_name);
  }
     }
     c = 0;
 } else {
     buf[c++] = ch;
 }
 if (c > sizeof(buf)-1) {
     exit(1);
 }
    }
    fclose(fd);
    return 0;
}
   
-------------------------------------------------------------------------------
Three new structure definitions were added:

struct hostent *he;
struct servent *srvc;
struct in_addr t_addr;

The current version of this program has reincorpated the gethostbyaddr()
routine from the first example so that DNS hostnames can be printed out as
well. The getservbyport() function was also added so that each port could be
matched with a standard service. Refer back to the first paper in this
series Coding a TCP Connect Port Scanner: Step by Step, if you need a
refresher on how these functions are implemented.

-------------------------------------------------------------------------------
The next paper in this series will demonstrate how to incorporate
Variable-length subnet masking (VLSM) into a port scanner.


             --nightcat

這篇文章假設讀者已經讀了truncode.org的文章Coding a TCP Connect Port Scanner: Step by Step 或者已經有了TCP/IP套接字編程的基本技能。此文是建立在Coding a TCP Connect Port Scanner: Step by Step這篇文章之上。

]介紹

internet給系統管理員,網絡滲透試驗者,放火牆分析師,和黑客提供了幾乎無數的安全掃描器。試一下在象http://packetstormsecurity.com這樣的站點上搜索一下,不會化多長時間就有大量很好的c代碼淹沒你。

多數的小端口和漏洞掃描器,僅僅每次掃描一個目標。這裏你有兩個選擇。你既可以循環這個程序用shell腳本,也可以自己修改一個原代碼。

如果你對寫自己的程序不滿意,可以借鑑編寫完美的程序的其中的函數來充實自己的程序。在這篇文章我採取這種方法。

因爲這篇文章是input/output例子,我選擇一個從[email protected]所寫的完美的snmp暴力破解掃描器onesixtyone 分離出一個 read_hosts().在未來某時,修改和提取存在的你偶遇的代碼之後,你自然會開始開發自己的程序而不必思考它。你不是很厲害的時候,用存在的代碼可以給自己帶來信心。事實上這也是最好的學習的方法。要做的僅是理解用到的每行代碼。


]文件輸入函數:

讓我們先熟悉打開文件的方法

---------------------------------------------------------------------------
fopen()
---------------------------------------------------------------------------

爲了用一個文件,我們先需要打開它,這個可以用fopen()來完成。這個函數的原型如下:
FILE *fopen (const char *path, const char *mode);

這個fopen()函數有兩個參數:目標文件的路徑和打開模式,打開模式決定我們將會如何打開它。如果成功打開,將返回一個文件指針。


---------------------------------------------------------------------------
fclose()
---------------------------------------------------------------------------

這個函數正如你所想的:關閉一個文件。這個函數被期待傳遞給一個文件指針。

---------------------------------------------------------------------------
fgetc()
---------------------------------------------------------------------------

這個函數每次讀取一個字符。這是個神奇的函數允許我們從一個ip地址或者主機列表中讀取。例如:每次 fgetc() 返回,我們簡單的增加到下一個字符,直到到達文件結尾。

以下的簡單程序用先前的函數打開一個文件和打印出每行。

---------------------------------------------------------------------------
#include <stdio.h>

int main(int argc, char *argv[])
{
    FILE *fd;
    char buf[100];
    char ch, *iplist = argv[1];
    int c;

    if ((fd = fopen(iplist, "r")) == 0) {
        printf("Error opening input file %s/n", iplist);
        exit(1);
    }
    c = 0; ch = 0;

    while((ch = fgetc(fd)) != EOF) {
        if (ch == '/n' || ch == ' ' || ch == '/t') {
            buf[c] = '/0';
     if (c > 0) {
  printf("%s/n", buf);
     }
     c = 0;
        } else {
     buf[c++] = ch;
        }
        if (c > sizeof(buf)-1) {
            exit(1);
        }
    }
    fclose(fd);
    return 0;
}
---------------------------------------------------------------------------

首先,我們定義一個文件指針*fd.這個fopen() 函數到那時打開一個文件通過關聯到文件指針流。但我們用fgetc()循環每個字符,這個程序檢查新行,空格或者tab鍵,以表示輸入的結束。當遇到一個新行,空格,或者tab,就打印一個字符串,和再次開始循環。這個程序不一定令人感興趣。但是他適合一個開始寫小的端口或者漏洞掃描器。


讓我們修改這個程序通過用gethostbyaddr()程序來替換printf()聲明。現在這個程序僅僅小小的調整就變的更有用。他能快速在一個大的的ip列表中反向dns查找。

---------------------------------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main(int argc, char *argv[])
{
    struct hostent *he;
    struct in_addr t_addr;
    FILE *fd;
    char buf[100];
    char ch, *iplist = argv[1];
    int c;

    if ((fd = fopen(iplist, "r")) == 0) {
        printf("Error opening input file %s/n", iplist);
        exit(1);
    }
    c = 0; ch = 0;

    while((ch = fgetc(fd)) != EOF) {
        if (ch == '/n' || ch == ' ' || ch == '/t') {
            buf[c] = '/0';
            if (c > 0) {

  t_addr.s_addr = inet_addr(buf);

  if ((he=gethostbyaddr((char*)&(t_addr.s_addr),
      sizeof(buf),AF_INET)) == NULL) {
      herror("gethostbyname");
      exit(1);
         }
  printf("%s/n", he->h_name);
     }
     c = 0;
 } else {
     buf[c++] = ch;
        }
 if (c > sizeof(buf)-1) {
     exit(1);
        }
    }
    fclose(fd);
    return 0;
}
---------------------------------------------------------------------------

  一看之下,這個程序看起來比前面哪個複雜,但是邏輯上是一樣的。用這個簡單的模板,我們能開發很多有用的程序。這次我們能換出gethostbyaddr()程序用一個簡單的tcp連接端口掃描函數。這個程序被轉換成一個能夠掃描整個子網的端口掃描器。


---------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#define TRUE    1
#define FALSE   0
#define SSH     22

/*
 * simple TCP port scanner
 */
int portscan(char *remote_ip, u_short port)
{
    int sock_fd;
    int state;
    struct sockaddr_in target;

    sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    memset(&target, 0, sizeof(target));
    target.sin_family   = AF_INET;
    target.sin_addr.s_addr = inet_addr(remote_ip);
    target.sin_port = htons(port);

    if(connect(sock_fd,(struct sockaddr *)&target,sizeof(target))==0)
    {
        state = TRUE;
    } else {
 state = FALSE;
    }
    close(sock_fd);
    return state;
}

int main(int argc, char *argv[])
{
    struct hostent *he;
    struct in_addr t_addr;
    FILE *fd;
    char buf[100];
    char ch, *iplist = argv[1];
    int c, i;

    if ((fd = fopen(iplist, "r")) == 0) {
 printf("Error opening input file %s/n", iplist);
 exit(1);
    }

    while((ch = fgetc(fd)) != EOF) {
 if (ch == '/n' || ch == ' ' || ch == '/t') {
     buf[c] = '/0';
     if (c > 0) {
  if((i = portscan(buf, SSH) == 0))
      printf("%s has port %d closed./n", buf, SSH);
  else
      printf("%s has port %d open./n", buf, SSH);
     }
     c = 0;
 } else {
     buf[c++] = ch;
 }
 if (c > sizeof(buf)-1) {
     exit(1);
 }
    }
    fclose(fd);
    return 0;
}
---------------------------------------------------------------------------

這裏似乎對fopen(), fclose(), 和fgetc()是無限制。作爲一個練習,擇取一些你喜歡的只限制一個命令行只有一個端口的掃描器和添加進代碼成爲新的。
目前這個端口掃描器只掃描一個端口。沒關係,這裏我們需要做的是在portscan()周圍增加一個簡單的循環和一對新的變量,就象下面:

---------------------------------------------------------------------------
int start, end, counter;

start = atoi(argv[2]);
end = atoi(argv[3]);
---------------------------------------------------------------------------

增加一個簡單的循環在portscan()函數週圍:

---------------------------------------------------------------------------
if (c > 0) {
    for(counter = start; counter <= end; counter++)
    {
 if((i = portscan(buf, counter) == 0))
     printf("%s has port %d closed./n", buf, counter);
 else
     printf("%s has port %d open./n", buf, counter);
    }
}

---------------------------------------------------------------------------


運行輸出結果如下:

modular@truncode$ ./portscan hostips.txt 1 25
192.168.1.100 has port 1 closed.
192.168.1.100 has port 2 closed.
192.168.1.100 has port 3 closed.
192.168.1.100 has port 4 closed.
192.168.1.100 has port 5 closed.
...

這看起來相當難看,有意義的去修改這個程序,因而他輸出一個ip地址和一個主機名列表,開放的端口,和服務。以下的例子可能是用子進程來處理。除這點外,在最後還出示了每個新的修改後的構造:

---------------------------------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#define TRUE    1
#define FALSE   0

/*
 * simple TCP port scanner function
 */
int portscan(char *remote_ip, u_short port)
{
    int sock_fd;
    int state;
    struct sockaddr_in target;

    sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    memset(&target, 0, sizeof(target));
    target.sin_family   = AF_INET;
    target.sin_addr.s_addr = inet_addr(remote_ip);
    target.sin_port = htons(port);

    if(connect(sock_fd,(struct sockaddr *)&target,sizeof(target))==0)
    {
 state = TRUE;
    } else {
 state = FALSE;
    }
    close(sock_fd);
    return state;
}

int main(int argc, char *argv[])
{
    struct hostent *he;
    struct servent *srvc;
    struct in_addr t_addr;
    FILE *fd;
    char buf[100];
    char ch, *iplist = argv[1];
    int c, i;
    int start, end, counter;

    start = atoi(argv[2]);
    end = atoi(argv[3]);

    if ((fd = fopen(iplist, "r")) == 0) {
 printf("Error opening input file %s/n", iplist);
 exit(1);
    }
    c = 0; ch = 0;

    while((ch = fgetc(fd)) != EOF) {
 if (ch == '/n' || ch == ' ' || ch == '/t') {
     buf[c] = '/0';
     if (c > 0) {

  t_addr.s_addr = inet_addr(buf);

  if ((he=gethostbyaddr((char*)&(t_addr.s_addr),
      sizeof(buf),AF_INET)) == NULL) {
      herror("gethostbyname");
      exit(1);
  }
  
  printf("/n");
  printf("Interesting ports on %s (%s)/n/n", he->h_name, buf);
  printf("port/tstate/tservice/n");
  for(counter = start; counter <= end; counter++)
  {   
      if((i = portscan(buf, counter) == 0))
   continue;
      else
   srvc = getservbyport(htons(counter), "tcp");
   printf("%d/tcp/topen/t%s/n", counter,
    (srvc == NULL)?"unknown":srvc->s_name);
  }
     }
     c = 0;
 } else {
     buf[c++] = ch;
 }
 if (c > sizeof(buf)-1) {
     exit(1);
 }
    }
    fclose(fd);
    return 0;
}
   
---------

增加了三個結構定義:
struct hostent *he;
struct servent *srvc;
struct in_addr t_addr;

  這個程序目前的版本已經從先前的例子中合併了gethostbyaddr(),因此DNS hostnames 也能被打印。getservbyport() 函數也別增加以致每個端口可以與標準服務相匹配。查閱第一篇文章Coding a TCP Connect Port Scanner: Step by Step。如果你需要複習這些函數是如何實現的。

這個系列的下篇文章將會示範怎樣合併可變長度的子網掩碼(VLSM)到一個端口掃描器裏。


三。附原文:

Coding a TCP Connect Port Scanner: Using Input Files
rev 1.0
- C Hacker's Handbook Series #2 -
 _                         _    
| |_ ___ _ _ ___ ___ ___ _| |___
|  _|  _| | |   |  _| . | . | -_|
|_| |_| |___|_|_|___|___|___|___|

* truncode security development *
http://truncode.org
modular <[email protected]>

This paper assumes the reader has read truncode.org's paper,
Coding a TCP Connect Port Scanner: Step by Step or already has basic
TCP/IP socket programming skills. This paper is intended to build upon the paper, Coding a TCP Connect Port Scanner: Step by Step.

] Introduction

The Internet offers an almost endless amount of security scanners to the
System Administrator, Network Penetration Tester, Firewall Analyst, and Hacker.
Try a few searches on a web site like http://packetstormsecurity.com and it
won't take long until the massive volume of C code available overwhelms you.

A majority of the smaller port and vulnerability scanners for example, will
scan only one target at a time. This leaves you with two choices. You can
either loop the program in a shell script or modify the source code itself.

If you don't yet feel comfortable with writing your own routines, look at some
more of the robust scanners out there with the existing functionality you
would like to see in your own tools. This is the approach I take in this paper.
For this paper's file input/output example I chose to rip the read_hosts()
function from the excellent snmp brute-force scanner onesixtyone written by
[email protected]. After some time modifying and tweaking existing
code you come across, you'll naturally begin to develop your own routines
without even thinking about it. It is a common belief that if you use existing
code, you are not "elite". This is actually the best way to learn. Just make
sure you understand every line you use!

] File Input Function:

Let us first get reacquainted with the process of opening files.

-------------------------------------------------------------------------------
fopen()
-------------------------------------------------------------------------------

In order to use a file, we first need to open it. This is accomplished with
the fopen() function. This function's prototype is as follows:

FILE *fopen (const char *path, const char *mode);

The fopen() function takes two arguments: the path of the target file and the
mode, which determines how we will be opening it. If we are successful fopen()
returns a FILE pointer.

-------------------------------------------------------------------------------
fclose()
-------------------------------------------------------------------------------

This function does exactly what you think it does: closes the file. This
function expects to be passed a FILE pointer.

-------------------------------------------------------------------------------
fgetc()
-------------------------------------------------------------------------------

This function reads in one character at a time. This is the magic function
that will allow us to read in a list of IP addresses or hostnames, for example.
Each time fgetc() returns, we simply increment to the next character until we
reach the end of the line.

The following simple program open's a file and prints each line using the
previous functions:
-------------------------------------------------------------------------------
#include <stdio.h>

int main(int argc, char *argv[])
{
    FILE *fd;
    char buf[100];
    char ch, *iplist = argv[1];
    int c;

    if ((fd = fopen(iplist, "r")) == 0) {
        printf("Error opening input file %s/n", iplist);
        exit(1);
    }
    c = 0; ch = 0;

    while((ch = fgetc(fd)) != EOF) {
        if (ch == '/n' || ch == ' ' || ch == '/t') {
            buf[c] = '/0';
     if (c > 0) {
  printf("%s/n", buf);
     }
     c = 0;
        } else {
     buf[c++] = ch;
        }
        if (c > sizeof(buf)-1) {
            exit(1);
        }
    }
    fclose(fd);
    return 0;
}
-------------------------------------------------------------------------------

First, we define the FILE pointer *fd. The fopen() function then opens a file
by associating a stream with the FILE pointer. As we loop through each
character using fgetc(), the  program checks for a newline, space, or tab
indicating the end of an entry. When a new line, space, or tab is encountered,
a string is printed and the loop starts again. This program isn't exactly
interesting, but it serves as a basis from which to begin writing a small port
or vulnerability scanner.

Let's modify this program by replacing the printf() statement with a
gethostbyaddr() routine. Now this program has become useful with only a few
adjustments. It can now do speedy reverse DNS lookups on a large list of IP
addresses.

---------------------------------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main(int argc, char *argv[])
{
    struct hostent *he;
    struct in_addr t_addr;
    FILE *fd;
    char buf[100];
    char ch, *iplist = argv[1];
    int c;

    if ((fd = fopen(iplist, "r")) == 0) {
        printf("Error opening input file %s/n", iplist);
        exit(1);
    }
    c = 0; ch = 0;

    while((ch = fgetc(fd)) != EOF) {
        if (ch == '/n' || ch == ' ' || ch == '/t') {
            buf[c] = '/0';
            if (c > 0) {

  t_addr.s_addr = inet_addr(buf);

  if ((he=gethostbyaddr((char*)&(t_addr.s_addr),
      sizeof(buf),AF_INET)) == NULL) {
      herror("gethostbyname");
      exit(1);
         }
  printf("%s/n", he->h_name);
     }
     c = 0;
 } else {
     buf[c++] = ch;
        }
 if (c > sizeof(buf)-1) {
     exit(1);
        }
    }
    fclose(fd);
    return 0;
}
-------------------------------------------------------------------------------

At first glance, this program looks much more complex than its predecessor,
but the logic is in fact the same. By using this simple template, we can
develop many useful programs. This time we can swap out the gethostbyaddr()
routine with a simple TCP connect port scanning function. Suddenly this
program is transformed into a port scanner capable of scanning whole
subnets!

-------------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#define TRUE    1
#define FALSE   0
#define SSH     22

/*
 * simple TCP port scanner
 */
int portscan(char *remote_ip, u_short port)
{
    int sock_fd;
    int state;
    struct sockaddr_in target;

    sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    memset(&target, 0, sizeof(target));
    target.sin_family   = AF_INET;
    target.sin_addr.s_addr = inet_addr(remote_ip);
    target.sin_port = htons(port);

    if(connect(sock_fd,(struct sockaddr *)&target,sizeof(target))==0)
    {
        state = TRUE;
    } else {
 state = FALSE;
    }
    close(sock_fd);
    return state;
}

int main(int argc, char *argv[])
{
    struct hostent *he;
    struct in_addr t_addr;
    FILE *fd;
    char buf[100];
    char ch, *iplist = argv[1];
    int c, i;

    if ((fd = fopen(iplist, "r")) == 0) {
 printf("Error opening input file %s/n", iplist);
 exit(1);
    }

    while((ch = fgetc(fd)) != EOF) {
 if (ch == '/n' || ch == ' ' || ch == '/t') {
     buf[c] = '/0';
     if (c > 0) {
  if((i = portscan(buf, SSH) == 0))
      printf("%s has port %d closed./n", buf, SSH);
  else
      printf("%s has port %d open./n", buf, SSH);
     }
     c = 0;
 } else {
     buf[c++] = ch;
 }
 if (c > sizeof(buf)-1) {
     exit(1);
 }
    }
    fclose(fd);
    return 0;
}
-------------------------------------------------------------------------------

There seem to be unlimited possibilities with fopen(), fclose(), and fgetc().
As an exercise, pick some of your favorite scanners that are limited to only
one host on the command line and hack the code into something new!  

So far this port scanner only scans one port. No problem. At this point all we
need to do is add a simple for loop around the portscan() function and a
couple new variables like so:
-------------------------------------------------------------------------------
int start, end, counter;

start = atoi(argv[2]);
end = atoi(argv[3]);
-------------------------------------------------------------------------------
And a simple for loop around the portscan() function:

-------------------------------------------------------------------------------

if (c > 0) {
    for(counter = start; counter <= end; counter++)
    {
 if((i = portscan(buf, counter) == 0))
     printf("%s has port %d closed./n", buf, counter);
 else
     printf("%s has port %d open./n", buf, counter);
    }
}

-------------------------------------------------------------------------------
The output is superfluous and is due for some tweaking:

modular@truncode$ ./portscan hostips.txt 1 25
192.168.1.100 has port 1 closed.
192.168.1.100 has port 2 closed.
192.168.1.100 has port 3 closed.
192.168.1.100 has port 4 closed.
192.168.1.100 has port 5 closed.
...

That looks rather ugly. It makes sense to modify the program so it outputs
one IP address and a list of hostnames, open ports, and services. The
following example may be child's play, but the point is to show how each new modification builds on the last:
-------------------------------------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#define TRUE    1
#define FALSE   0

/*
 * simple TCP port scanner function
 */
int portscan(char *remote_ip, u_short port)
{
    int sock_fd;
    int state;
    struct sockaddr_in target;

    sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    memset(&target, 0, sizeof(target));
    target.sin_family   = AF_INET;
    target.sin_addr.s_addr = inet_addr(remote_ip);
    target.sin_port = htons(port);

    if(connect(sock_fd,(struct sockaddr *)&target,sizeof(target))==0)
    {
 state = TRUE;
    } else {
 state = FALSE;
    }
    close(sock_fd);
    return state;
}

int main(int argc, char *argv[])
{
    struct hostent *he;
    struct servent *srvc;
    struct in_addr t_addr;
    FILE *fd;
    char buf[100];
    char ch, *iplist = argv[1];
    int c, i;
    int start, end, counter;

    start = atoi(argv[2]);
    end = atoi(argv[3]);

    if ((fd = fopen(iplist, "r")) == 0) {
 printf("Error opening input file %s/n", iplist);
 exit(1);
    }
    c = 0; ch = 0;

    while((ch = fgetc(fd)) != EOF) {
 if (ch == '/n' || ch == ' ' || ch == '/t') {
     buf[c] = '/0';
     if (c > 0) {

  t_addr.s_addr = inet_addr(buf);

  if ((he=gethostbyaddr((char*)&(t_addr.s_addr),
      sizeof(buf),AF_INET)) == NULL) {
      herror("gethostbyname");
      exit(1);
  }
  
  printf("/n");
  printf("Interesting ports on %s (%s)/n/n", he->h_name, buf);
  printf("port/tstate/tservice/n");
  for(counter = start; counter <= end; counter++)
  {   
      if((i = portscan(buf, counter) == 0))
   continue;
      else
   srvc = getservbyport(htons(counter), "tcp");
   printf("%d/tcp/topen/t%s/n", counter,
    (srvc == NULL)?"unknown":srvc->s_name);
  }
     }
     c = 0;
 } else {
     buf[c++] = ch;
 }
 if (c > sizeof(buf)-1) {
     exit(1);
 }
    }
    fclose(fd);
    return 0;
}
   
-------------------------------------------------------------------------------
Three new structure definitions were added:

struct hostent *he;
struct servent *srvc;
struct in_addr t_addr;

The current version of this program has reincorpated the gethostbyaddr()
routine from the first example so that DNS hostnames can be printed out as
well. The getservbyport() function was also added so that each port could be
matched with a standard service. Refer back to the first paper in this
series Coding a TCP Connect Port Scanner: Step by Step, if you need a
refresher on how these functions are implemented.

-------------------------------------------------------------------------------
The next paper in this series will demonstrate how to incorporate
Variable-length subnet masking (VLSM) into a port scanner.

 

發佈了37 篇原創文章 · 獲贊 1 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章