hacker成长的代码之路:扫描(1)

发表于:2007-07-04来源:作者:点击数: 标签:
作者:kf_701 写作时间:2005/4 Email:kf_701@21cn.com 要求的专业知识: 一: 精通OSI参考模型,精通 网络 五层:物理层,数据链路层, 网络 层,传输层,应用层。 精通每一层的协议,数据报格式。精通 网络 拓扑结构,第一层,第二层,第三层的网 络互联,
作者:kf_701 写作时间:2005/4 Email:kf_701@21cn.com

要求的专业知识
    一:    精通OSI参考模型,精通网络五层:物理层,数据链路层,网络层,传输层,应用层。
        精通每一层的协议,数据报格式。精通网络拓扑结构,第一层,第二层,第三层的网
        络互联,数据的转发和路由等。
    二:    精通C语言程序设计,UNIX/LINUX程序设计,网络程序设计。熟悉UNIX/LINUX系
        统操作,熟悉著名服务的基本配置,特性及使用的端口号。熟悉经典网络命令的使用,
        如:netstat,ping,traceroute,netcat,arp等。
    三:    精通标准SQL语言,熟悉流行的数据库使用,如:Oracle,Mysql等。掌握数据库与
        WEB语言的结合使用。

  扫描通常是查看目标主机是否开机,对外开放了哪些服务及端口,服务的版本号,使用的是什么操作系
统及版本号。

  更进一步的扫描通常是获得目标主机的系统漏洞,各种开放的服务漏洞,而这种漏洞扫描通常都是用程
序从一个已知的漏洞数据库里依次尝试。这里就跳过而不给出示例代码了。用于这类扫描的常用工具有:
whisker(www.wiretrip.net),nikto(www.cirt.net)等,有兴趣的读者可以自行下载其源码阅
读。不过建议要有一点perl语言的基础。

  现在来讲通常的端口扫描。使用传输层协议(TCP and UDP)的应用,都会开一个端口号来listen,如
web通常开放80,ftp用21,telnet用23(现在几近不用了),ssh用22等。最简单的思想就是尝试去连
接目标主机的端口,如果可以建立连接,可推测某端口开放,如果是著名端口,则相应的服务是打开的。


  我所要讲的是如何写程序去试图连接目标主机的目标端口。主要的方式有TCP和UDP。

  1:例如我们给目标主机上的某个端口发送一个UDP数据报,如果目标端口没有开放,则目标主机会回应
    一个端口不可达ICMP报文,当我们的程序收到了这个ICMP报文,则可认为此端口没有开放。这样依次
    连接每个端口,则可判断目标主机开放了哪些端口了。

  2:单纯的TCP方式扫描大概是最简单的了。建一个socket,然后调用connect去连接目标主机上的端口,
    成功返回,则可判断端口开放。


下面是一个TCP方式扫描的示例程序代码,代码在linux测试成功。编译命令:clearcase/" target="_blank" >cc -pthread scan.c。

/*
   A very beautiful port scan program
   Copyright (C) 2005  by kf_701

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

   The author can be reached as follows:
        E_mail: kf_701@21cn.com
        Address:        hefei of china
        Phone:  0551-2150103
*/

/*
 * 本程序是一个简单的端口扫描程序,使用TCP SOCKET
 * 依次试连接目标主机的端口,以此判断端口开放与否。
 * 程序设计上使用了线程池,共100个线程。每个线程
 * 用mutex方式来得到一个端口号用来测试,并将端口
 * 号变量port_num加 1,当port_num增至 MAX_PORT,线程
 * 将thread_num减 1并结束自身,当99个线程都结束后,
 * thread_num变为 0,最后一个线调用exit结束程序。
 */

#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<netdb.h>
#include<netinet/in.h>
#include<unistd.h>
#include<errno.h>
#include<signal.h>
#include<sys/socket.h>
#include<stdlib.h>
#include<pthread.h>
#include<thread_db.h>
#include<arpa/inet.h>

#define MAX_PORT        4000
#define MAX_THREAD      100
/* basename(argv[0]). netBSD,linux and gnu libc all define it. */
extern char *__progname;

/* globals */
int port_num,thread_num=100;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

int main(int argc,char **argv)
{
        void *thread_main(void *);
        pthread_t tid;

#ifndef HAVE__PROGNAME
__progname = argv[0];
#endif
        printf("----kf_701 scan tool---------\n");

        if(argc != 2){
                fprintf(stderr,"*  Usage: %s host\n",__progname);
                exit(0);
        }

        /* create threads */
        int i;
        for(i=0;i<MAX_THREAD;i++)
                pthread_create(&tid,NULL,&thread_main,argv[1]);
        /* main pause */
        for( ; ; )
                pause();
}

void *thread_main(void *arg)
{
        struct sockaddr_in sa;
        struct hostent *host;
        int i,sockfd;

        pthread_detach(pthread_self());

        /* init sockaddr_in struct */
        bzero(&sa,sizeof(struct sockaddr));
        sa.sin_family = AF_INET;
        if(inet_aton((char *)arg,&sa.sin_addr) == 0){
                host = gethostbyname((char *)arg);
                if(host == NULL){
                        fprintf(stderr,"Hostname Error: %s \n",hstrerror(h_errno));
                        exit(1);
                }
                sa.sin_addr = *(struct in_addr *)(host->h_addr_list[0]);
        }

  while(1){
        /* get a port number */
        if(pthread_mutex_lock(&mutex) != 0) exit(1);
        if(++port_num > MAX_PORT){
                if(pthread_mutex_unlock(&mutex) != 0) exit(1);
                if(--thread_num == 0){
                        printf("-----------------------------\n");
                        exit(0);
                }
                pthread_exit(NULL);
        }
        i=port_num;
        if(pthread_mutex_unlock(&mutex) != 0) exit(1);
        /* try to connect */
        sa.sin_port=htons(i);
        sockfd = socket(AF_INET,SOCK_STREAM,0);
        if(sockfd < 0)
                perror("Create socket error"),exit(1);
        if(connect(sockfd,(struct sockaddr *)&sa,sizeof(sa)) == 0)
                printf("* port %d is open \n",i);
        if(close(sockfd) < 0)
                perror("shutdown error"),exit(1);
  }/*end while*/
}


如果你深入学习过TCP/IP协议的话,就会知道建立TCP连接的三次握手(SYN-->SYN/ACK-->ACK),
下面的文章,我们将学习半开式的扫描。如发送一个SYN位设置为1的数据报,当收到目标主机的ACK
回应,则可判断某端口是开放的。然后我们立即给目标主机发送一个RST位置1的数据报,关上正在建
立连接的三次握手。这是一种比较隐匿的扫描方式,因为很多主机不会记录没有完成三次握手的事件。
******待续******

原文转自:http://www.ltesting.net