freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

nmap-1.51 源码解析 - 前言 && 端口的选择解析
2022-12-11 01:26:48
所属地 江西省

为什么阅读源码?

为了加深对nmap的了解,与其相关核心扫描技术的学习,决定阅读其源码,并将其中的扫描模块与各个辅助模块拿出来单独实现,一方面学习其编码风格、一方面复用其相关实现到自己的代码中

此处先选定为nmap最初版本的C语言版本的源码nmap-1.51,一方面因为其结构更加鲜明,一方面更易理解其底层的框架

阅读的方式

此处并不打算一上来先说其整体框架,直接列一大堆文件、函数之间的依赖关系会显得非常劝退,与其如此,不如依main函数的执行顺序为模板(或以nmap中提供的参数为模块),依次拿出其中每个小模块对应的函数说明,并将其单独拿出,实现其功能,这样不仅可以在我们自己的代码中借鉴其思路,复用这些小模块,而且在每一个小模块逐渐的实现过程中,就会对整体的框架慢慢有了很深的了解

nmap-1.51 提供的参数

对于其提供的参数,有一个大概了解即可,后面会慢慢展开

nmap V. 1.51 usage: ./nmap [options] [hostname[/mask] . . .] options (none are required, most can be combined):
   -t tcp connect() port scan
   -s tcp SYN stealth port scan (must be root)
   -U Uriel Maimon (P49-15) style FIN stealth scan.
   -P ping "scan". Find which hosts on specified network(s) are up.
   -b <ftp_relay_host> ftp "bounce attack" port scan
   -u UDP port scan, will use MUCH better version if you are root
   -l Do the lamer UDP scan even if root.  Less accurate.
   -f use tiny fragmented packets for SYN or FIN scan.
   -D Don't ping hosts (needed to scan www.microsoft.com and others)
   -i Get identd (rfc 1413) info on listening TCP processes.
   -p <range> ports: ex: '-p 23' will only try port 23 of the host(s) '-p 20-30,63000-' scans 20-30 and 63000-65535 default: 1-1024
   -F fast scan. Only scans ports in /etc/services, a la strobe(1).
   -n Don't DNS resolve anything unless we have to (makes ping scans faster)
   -L <num> Number of pings to perform in parallel.  Your default is: 52
   -o <logfile> Output scan logs to <logfile>.
   -R Try to resolve all hosts, even down ones (can take a lot of time)
   -r do NOT randomize target port scanning order.
   -S If you want to specify the source address of SYN or FYN scan.
   -T <seconds> Set the ping and tcp connect() timeout.
   -v Verbose.  Its use is recommended.  Use twice for greater effect.
   -h help, print this junk.  Also see <http://www.dhp.com/~fyodor/nmap/>
   -V Print version number and exit.
   -w <n> delay.  n microsecond delay. Not recommended unless needed.
   -M <n> maximum number of parallel sockets.  Larger isn't always better.
   -q quash argv to something benign, currently set to "pine". (deprecated) Hostnames specified as internet hostname or IP address.  Optional '/mask' specifies subnet. cert.org/24 or 192.88.209.5/24 or 192.88.209.0-255 scan CERT's Class C.

The Art of Port Scanning

此处附上其作者 Fyodor给出的介绍

Scanning, as a method for discovering exploitable communication channels, has been around for ages. The idea is to probe as many listeners as possible, and keep track of the ones that are receptive or useful to your particular need. Much of the field of advertising is based on this paradigm, and the "to current resident" brute force style of bulk mail is an almost perfect parallel to what we will discuss. Just stick a message in every mailbox and wait for the responses to trickle back.

Scanning entered the h/p world along with the phone systems. Here we have this tremendous global telecommunications network, all reachable through codes on our telephone. Millions of numbers are reachable locally, yet we may only be interested in 0.5% of these numbers, perhaps those that answer with a carrier.

The logical solution to finding those numbers that interest us is to try them all. Thus the field of "wardialing" arose. Excellent programs like Toneloc were developed to facilitate the probing of entire exchanges and more. The basic idea is simple. If you dial a number and your modem gives you a CONNECT, you record it. Otherwise the computer hangs up and tirelessly dials the next one.

While wardialing is still useful, we are now finding that many of the computers we wish to communicate with are connected through networks such as the Internet rather than analog phone dialups. Scanning these machines involves the same brute force technique. We send a blizzard of packets for various protocols, and we deduce which services are listening from the responses we receive (or don't receive).

具体每个功能作者所附上的介绍将会在阅读到对应的代码时附上

代码执行顺序

一句话:从nmap.c中的main函数开始执行,非常明了

最后

由于实力水平有限,对于源码中某些解读若与其用意出现偏差,也请大家及时指正

Part 1 :有关端口的处理 

nmap中若要对目标进行扫描,那么要扫描哪些端口?端口的指定是一个很重要的问题,在该模块中将介绍nmap中提供的两个端口处理函数:getfastscanport()以及getpts(),本模块只介绍初始化时端口的获取与指定,至于后续操作中对端口的 删除/增加/查看 将在后续模块中说明

二者最主要的区别在于:

  • getfastscanport()获取端口的方式是通过附加-F参数由nmap从系统中的/etc/services中获取
  • getpts()这种端口的获取方式由使用者通过-p进行指定

要注意的一点是:-f-F参数是不可以同时出现的,否则nmap将无法确认如何获取端口,在源码中作者也特别提醒了这个问题:

1668145244_636de05c6aec7b4093353.png!small?1668145243789

if (fastscan && ports)
{
fatal("You can use -F (fastscan) OR -p for explicit port specification.Not both!\n");
}

接下来将会挨个说明这两个函数是如何实现的,同时这两个函数中还会涉及到一些相关的辅助函数,也会一并介绍以下,这些辅助函数包括但不限于:

  • safe_malloc()作者对 malloc 进行了一个非常简单的封装
  • fatal()将出的错误打印到控制台

1.1 - getfastscanport()

下定义

所谓的-F也就是fast scan(快速扫描)只是根据是TCP还是UDP在端口的选择上做出了优化,扫描模式上依旧要配合其提供的几种扫描模式

源码中的位置

1668145437_636de11d189f21b27253c.png!small?1668145436599

基本介绍

nmap是可以指定端口的,若不想自己指定端口,可以加上-F参数做一个快速扫描,那么本节就通过看其源码看看,所谓的 快速扫描端口是如何获取选定的

参数的解析

short fastscan=0
......
......
case 'F': fastscan++; break;

代码中对于该参数为其设立了一个” 标志位” :fastscan,当附加-F参数后,便会将fastscan加为1,在之后对参数解析完成后,若fastscan = 1则会进入到其对应的处理函数中进行端口的获取

相关处理函数

getfastports

unsigned short *ports = NULL;
......
......
if (fastscan)
{
	  ports = getfastports(o.synscan|tcpscan|o.fragscan|o.finscan|bouncescan,o.udpscan|o.lamerscan);
}

此处-F所对应的处理函数getfastports的参数可能有些复杂,但可以看出其共有两个参数,每个参数都将许多项做了位或|运算,也就是说两个参数最终各自只有两种结果0 / 1

那么只要将其原型拿来就很好理解了:unsigned short *getfastports(int tcpscan, int udpscan)

所以很容易发现,其参数说白了只是为了区分当前要进行的是TCP端口的扫描,还是UDP端口的扫描:

  • 第一个参数置1TCP端口扫描
  • 第二个参数置1UDP端口扫描

那么到底进行那种扫描呢,就要根据附加的其他参数来确定了,这个会一点点说到,目前我们要做的是深入该函数,看看其针对不同的扫描是如何确认其端口的

起始稍微了解nmap就可以看出,其中第一个参数处对应的是几种TCP类扫描的模式,同样第二个参数对应了几种UDP扫描的模式

getfastports函数的具体实现

源码

unsigned short *getfastports(int tcpscan, int udpscan) 
{
    int portindex = 0, res, lastport = 0;
    unsigned int portno = 0;
    unsigned short *ports;
    char proto[10];
    char line[81];
    FILE *fp;

		// 这里的 safe_malloc 是作者写的一个简单捕获错误的 malloc ,防止分配失败
		// 该函数会在稍后进行说明
    ports = safe_malloc(65535 * sizeof(unsigned short));
    proto[0] = '\\0';

		// 打开 /etc/services 文件
    if (!(fp = fopen("/etc/services", "r"))) 
		{
        printf("We can't open /etc/services for reading!  Fix your system or don't use -f\\n");
        perror("fopen");
        exit(1);
    }
    
		// 挨个行处理
    while(fgets(line, 80, fp)) 
		{
        res = sscanf(line, "%*s %u/%s", &portno, proto);
        if (res == 2 && portno != 0 && portno != lastport) 
        { 
            lastport = portno;
            if (tcpscan && proto[0] == 't')
                ports[portindex++] = portno;
            else if (udpscan && proto[0] == 'u')
                ports[portindex++] = portno;
        }
    }

		
		// 此处 o 为一个全局结构体 ops 的实例化对象,该结构体对象中存储着一些基本信息
    o.numports = portindex;
    ports[portindex++] = 0;

		// 最终加入该 ports 数组的端口肯定 < 65535 ,所以返回时调整其大小
    return realloc(ports, portindex * sizeof(unsigned short));
}

总体思路

读取Linux中的/etc/services文件,获取其中的TCP / UDP端口,但并不是获取所有的TCPUDP端口

  • 若选择TCP端口:几乎获取/etc/services中的所有TCP端口

  • 若选择UDP端口:由于UDP端口扫描普遍的时间需要更长,所以不会获取/etc/services中的所有UDP端口,而是获取那些相同端口没有同时包含TCPUDP服务的端口

    比如对于DNS服务,在53端口既有建立在TCP协议的又有建立在UDP协议的,此时UDP扫描便会忽略该端口

并将最后获取到的所有端口保存于其内部开始在堆区分配的一个ports“数组” 中进行返沪

相关变量的含义

  • portindex- 用于指示ports“数组” 的下标,将拿到的端口依次放到下标处

  • res-sscanf函数读取的参数个数

  • lastport- 上一次加入数组的端口

  • portno- 每一次得到的即将加入ports数组的端口号

  • proto[10]- 字符串数组,保存字符串"tcp""udp"

    此处有一个很有意思的开发小记录,会在一会说

  • line[81]- 用于读取/etc/services中的每一行

关键代码理解

while(fgets(line, 80, fp)) 
		{
        res = sscanf(line, "%*s %u/%s", &portno, proto);
        if (res == 2 && portno != 0 && portno != lastport) 
        { 
            lastport = portno;
            if (tcpscan && proto[0] == 't')
                ports[portindex++] = portno;
            else if (udpscan && proto[0] == 'u')
                ports[portindex++] = portno;
        }
    }

相信上述代码并不难理解,所以此处说几个点即可:

  • %*s的用意:该格式用在sscanf会忽略掉%u/%s前的字符串,防止其被读取

    由于/etc/services中会有一些以#开头的注释信息或具体服务的名称,所以该格式就是为了忽略这些

    1668067008_636caec097bf8999eb30d.png!small?1668067009286

  • if (res == 2 && portno != 0 && portno != lastport)

    其中关于UDP端口的选择,正是该语句实现的,由于其中限定了portno != lastport,而在if语句中的第一句是lastport = portno所以对于前一个出现过的端口是不会再次进入该if的,又因为/etc/services文件中,出现相同的端口几乎都是TCPUDP上方,所以这样就导致UDP端口数组中会略去那些通信存在TCP协议的端口

但这种处理方式还是有些粗糙的,当重新运行该功能时,对于TCP的协议其会遗漏两个协议(不知是有意为之还是对数据过滤的不严谨),而且可以很明显的发现其并没有存储协议的类型,刚刚提到一个有意思的地方就在此处:

2000年时Dion Stempfley发了一个关于 该函数的设计思路,原文是这样的:

Currently, thegetfastports()will get a single array of all of the ports to be scanned. My problem is that since UDP scans can take a lot longer, I want to limit that part of the scan to a very small number of key services.

*The ports array is an unsigned short , so there is no room to store oddball info like the protocol. I thought about a screwy mechanism of using a negative value for udp, but thought that would get errorprone, and would still require change to the structure. I then thought about doing a separate array for tcp and udp ports.

Any thoughts on how best to do this? Has anyone else thought about the problem? Is it worth pursuing?

我的问题是,由于UDP扫描可能需要更长的时间,我想将扫描的那部分限制为非常少的关键服务。端口数组是一个unsigned short *,因此没有空间来存储像协议这样的古怪信息。我想到了一种为udp使用负值的诡异机制,但认为这很容易出错,并且仍然需要改变结构。然后我考虑为tcpudp端口做一个单独的数组。关于如何最好地做到这一点的任何想法?有没有其他人考虑过这个问题?值得追求吗?

可以看出其对UDP端口的选择也是费尽心思,透露出其起始非常不想对UDP的所有端口进行扫描,而且将UDPTCP端口信息分离,所以才在参数处做出了区分,

safe_malloc

void fatal(char *fmt, ...)
{
        va_list  ap;
        va_start(ap, fmt);
        fflush(stdout);
        vfprintf(stderr, fmt, ap);
        fprintf(stderr, "\\n");
        va_end(ap);
        exit(1);
}

void *safe_malloc(int size)
{
  void *mymem;
  if (size < 0)
    fatal("Tried to malloc negative amount of memmory!!!");
  if ((mymem = malloc(size)) == NULL)
    fatal("Malloc Failed! Probably out of space.");
  return mymem;
}

这是作者为了防止出现错误异常而对malloc的一个简单的包装,非常的简单,无需过多解释

其中调用的 fetal 函数也不难理解,作用就是想标准错误输出错误语句

本地使用该模块

直接用gcc进行编译即可:gcc get_fastscan_ports.c -o get_fastscan_ports

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>

/* global options */
struct ops o; 

// 该结构体便是之前所说的用于保存基本信息的结构体
// 不必一下知道其参数全部含义,慢自然会全部通晓
struct ops /* someone took struct options, <grrr> */ {
  int debugging;
  int verbose;
  int spoofsource; /* -S used */
  int number_of_ports;
  int max_sockets;
  int isr00t;
  int identscan;
  int dontping;
  int allowall;
  int wait;
  int ptime;
  int numports;    // 从 /etc/services 中获得的端口个数
  int fragscan;
  int synscan;
  int finscan;
  int udpscan;
  int lamerscan;
  int noresolve;
  int force; /* force nmap to continue on even when the outcome seems somewhat certain */
  FILE *logfd; /* Output log file descriptor */
};

void fatal(char *fmt, ...)
{
        va_list  ap;
        va_start(ap, fmt);
        fflush(stdout);
        vfprintf(stderr, fmt, ap);
        fprintf(stderr, "\\n");
        va_end(ap);
        exit(1);
}

void *safe_malloc(int size)
{
  void *mymem;
  if (size < 0)
    fatal("Tried to malloc negative amount of memmory!!!");
  if ((mymem = malloc(size)) == NULL)
    fatal("Malloc Failed! Probably out of space.");
  return mymem;
}

unsigned short *getfastports(int tcpscan, int udpscan)
{
    int portindex = 0, res, lastport = 0;
    unsigned int portno = 0;
    unsigned short *ports;
    char proto[10];
    char line[81];
    FILE *fp;
    ports = safe_malloc(65535 * sizeof(unsigned short));
    proto[0] = '\\0';
    if (!(fp = fopen("/etc/services", "r")))
    {
        printf("We can't open /etc/services for reading!  Fix your system or don't use -f\\n");
        perror("fopen");
        exit(1);
    }

    while(fgets(line, 80, fp)) {
        res = sscanf(line, "%*s %u/%s", &portno, proto);
        if (res == 2 && portno != 0 && portno != lastport) {
        lastport = portno;
        if (tcpscan && proto[0] == 't')
        ports[portindex++] = portno;
        else if (udpscan && proto[0] == 'u')
        ports[portindex++] = portno;
        }
    }

    o.numports = portindex;
    ports[portindex++] = 0;
    return realloc(ports, portindex * sizeof(unsigned short));
}

// main 函数以下为自行构造
int main(int argc,char **argv)
{

        short fastscan = 1;
        int _i;

        if (fastscan)
        {
                 unsigned short *ports = NULL;

                 // ports = getfastports(o.synscan|tcpscan|o.fragscan|o.finscan|bouncescan,o.udpscan|o.lamerscan);

                 ports = getfastports(atoi(argv[1]),atoi(argv[2]));

                 for (_i = 0; _i < o.numports; _i++)
                {
                        fprintf(stdout,"port[%d]:%d\\n",_i,ports[_i]);
                }
        }

}

该程序根据传入的参数选择 进行 TCP 端口的选择还是 UDP 端口的选择

TCP 端口获取

./get_fastscan_ports 1 0

1668067028_636caed4b18b775c2e634.png!small?1668067029360

统计使用该模块函数获取到的函数端口个数:

1668067034_636caedab10c1949d82e9.png!small?1668067035501

但实际上/etc/servicesTCP协议的端口数量

1668067038_636caede0a681da4cca83.png!small?1668067038432

发现有区别,分别将二者得到的端口号过滤保存到文件中,并用diff对比得出

1668067040_636caee0b87ad5f8289be.png!small?1668067041287

通过getfastports得到的TCP协议的端口缺失了750751端口

经观察发现,缺失的原因是因为只有这两个TCP端口所在的位置是先UDPTCP的,所以会被过滤掉,但该源码历史久远,当时的情况已无法具体得知,知道差别即可

UDP 端口获取

./get_fastscan_ports 0 1

1668067049_636caee9785132d901d19.png!small?1668067049914

1.2 -getpts()

下定义

通过-p参数指定端口时调用的就是该函数

源码中的位置

1668145508_636de164b6cf562eff251.png!small?1668145509343

基本介绍

-p支持以下几种端口的指定方式:

  • -p200-1024指定端口区间200 ~ 1024
  • -p8080指定单端口8080
  • -p8080,-255不仅指定了一个单端口,还指定了一个前端口的区间范围1 ~ 255
  • -p8080,-255,60000-在上面的基础上,指定了一个后端口区间60000 ~ 65535

相关处理函数

getpts

case 'p': 
    if (ports)
		{
				fatal("Only 1 -p option allowed, seperate multiple ranges with commas.");			
		}
		ports = getpts(optarg); break;
      
    

当指定该 -p 参数时,会先进行判断,若ports数组已经被使用了( 也就表明之前已经指定过了-p参数)则不允许再次指定-p

之后将-p参数后接的一串用户指定端口的字符串作为参数传入getpts()函数中进行处理

若对optarg了解,详情见man getopt

getpts函数的具体实现

来自作者的小吐槽

初看该函数的名字getpts我们可能有点不知所云,不知这个pts代表了什么,为什么不将他命名为getports()呢?简单易懂

作者在nmap.h中关于该函数的声明后给出注释写到 / someone stole the name getports()! / ,也许是哪里使用了该名字 (手动笑哭),具体的历史原因就由大家去探索吧

1668145531_636de17b695944d43ec4e.png!small?1668145530620

源码

/* Convert a string like "-100,200-1024,3000-4000,60000-" into an array 
   of port numbers*/
unsigned short *getpts(char *origexpr) 
{
	int exlen = strlen(origexpr);
	char *p,*q;
	unsigned short *tmp, *ports;
	int i=0, j=0,start,end;
    
  // 将 origexpr 的端口字符串在堆区复制一份并将指针给 expr
	char *expr = strdup(origexpr);
	char *mem = expr;

  // 由于端口可能指定 1-65535 所以先分配 65535 块 short 空间
	ports = safe_malloc(65536 * sizeof(short));

  // 去掉端口字符串中的所有空格,将所有非空格的字符都移到字符串开头
	for(;j < exlen; j++) 
  {
        if (expr[j] != ' ') 
        {
            expr[i++] = expr[j];
        }
            
  }
  	
  // 截断字符串到空格开始处,节省空间
	expr[i] = '\\0';
    
  // 去掉空格后字符串的长度
	exlen = i;
    
  // 重置 i
	i=0;
    
  // 在端口字符串中寻找 ,
  // 因为端口的选择格式之一是 -p20,5050,21
	while((p = strchr(expr,','))) 
  {
        // 将 , 替换为 \\0
  	    *p = '\\0';
        
        /*
        	对应端口指定示例 -100
        	目的:遍历 1 ~ 100 端口
        	实现见下第一个 if
        */
  	    if (*expr == '-') 
        {
            // 将端口的开始置为 1
            start = 1; 
            // 由于刚刚将 , 替换为了 \\0
            // 所以 expr + 1 就是跳过字符 - 得到后面跟着的终止端口(100)
            // 所以就是将起始端口设为 1 终止端口设为 100
            end = atoi(expr+ 1);
        }
        /*
        	若不是以 - 开头,就存在以下一种情况
        	1:单端口
        	2:指定范围的端口 eg. 20-25
        	3:指定了起始端口,遍历后续所有端口 eg. 6000-
        */
  	    else 
        {
            
		    		start = end = atoi(expr);
		            // 指定范围的端口 eg. 20-25
		            /*
		            	经过前面的判断可以知道当前起始字符不是 - ,所以接下来要判断
		            	在后面是否出现了 - ,并且 - 的下一个字符不是空,若不为空则表
		            	名当前制定了一个端口的范围
		            	eg. 20-25
		            */
		    		if ((q = strchr(expr,'-')) && *(q+1) )
		        {
		                // 将终止端口设为 - 的下一个字符,也就是 25
		                end = atoi(q + 1);
		                
		                // 此时 start = 20 end = 25
		         }
		        // 该端口后面的所有端口(65535)
		    		else if (q && !*(q+1)) 
		            {
		                // 指定了起始端口,遍历后续所有端口 eg. 6000-
		                // 若 q 为 - 且 q+1 = 空表示遍历后续所有端口
		                // 即 6000-65535     
		                end = 65535;
		            }
                
				 }
			  // 若指定了调试选项则会打印被选中的端口
			  if (o.debugging)
			  {
			     printf("The first port is %d, and the last one is %d\\n", start, end);
			  }   
			        
			  // 异常捕获
			  if (start < 1 || start > end) 
			  {
			      fatal("Your port specifications are illegal!");
			  }
			        
			  // 将选中的端口放入 ports 端口数组中
			  for(j=start; j <= end; j++) 
			  {
			     ports[i++] = j;
			  }
			    
			  // 将最后一个 , 的下一个字符的位置赋给 expr
			  expr = p + 1;
	}
    
  // 上述的处理无法处理到最后一个 , 后面的内容,所以需要特别处理
  // 处理方式与上相同
	if (*expr == '-') 
    {
  		start = 1;
  		end = atoi(expr+ 1);
	}
	else 
  {
	  		start = end = atoi(expr);
	  		if ((q =  strchr(expr,'-')) && *(q+1) ) 
	      {
	            end = atoi(q+1);
	      }
	  		else if (q && !*(q+1)) 
	      {
	            end = 65535;
	      }
  }
  if (o.debugging)
  {
      printf("The first port is %d, and the last one is %d\\n", start, end);
  }
      
  if (start < 1 || start > end) 
  {
      fatal("Your port specifications are illegal!");
  }
  for(j=start; j <= end; j++) 
  {
      // 继续从 i 位置向 ports 数组中添加端口
      ports[i++] = j;
  }
    
  // 将最终添加的所有端口的数量赋给全局结构体 ops 中的numports成员
  o.numports = i;

  // 端口数组以 0 结尾
  ports[i++] = 0;
    
  // 最初分配的 65536 空间若大于最终实际需要的空间,则进行缩小重新分配
  tmp = realloc(ports, i * sizeof(short));
  free(mem);
  return tmp;
}

具体思路

具体的思路在上述的代码中已给出,思路其实不难,就是通过strchr()函数来匹配一些特殊的字符:

  • 比如说,由于该符号是用户输入指定端口时用于分隔不同端口的分隔符,所以通过定位该符号的位置就可以确认每一个指定不同端口的位置
  • 又比如说-通过该符号位于当前指定端口x前还是端口后来确认要加入最终端口”数组”的是:1 ~ x还是x ~ 65535

详细看上述代码中的注释即可

本地使用该模块

直接用gcc进行编译即可:gcc getportscan.c -o getportscan

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdarg.h>

void fatal(char *fmt, ...)
{
        va_list  ap;
        va_start(ap, fmt);
        fflush(stdout);
        vfprintf(stderr, fmt, ap);
        fprintf(stderr, "\\n");
        va_end(ap);
        exit(1);
}

/* Convert a string like "-100,200-1024,3000-4000,60000-" into an array 
   of port numbers*/
unsigned short *getpts(char *origexpr)
{
        int exlen = strlen(origexpr);
        char *p,*q;
        unsigned short *tmp, *ports;
        int i=0, j=0,start,end;

    
        char *expr = strdup(origexpr);
        char *mem = expr;

    
        ports = malloc(65536 * sizeof(short));

   
        for(;j < exlen; j++)
        {
                 if (expr[j] != ' ')
                 {
                         expr[i++] = expr[j];
                 }

         }

        
        expr[i] = '\\0';

   
        exlen = i;

 
        i=0;

  
        while((p = strchr(expr,',')))
         {
 
            *p = '\\0';

            if (*expr == '-')
            {
                 start = 1;
                 end = atoi(expr+ 1);
             }
            else
           {

                start = end = atoi(expr);

                if ((q = strchr(expr,'-')) && *(q+1) )
                {

                         end = atoi(*(q + 1));
                 }

                else if (q && !*(q+1))
                { 
                         end = 65535;
                }

                }
                if (1)
                {
                        printf("The first port is %d, and the last one is %d\\n", start, end);
                }

                if (start < 1 || start > end)
                {
                        fatal("Your port specifications are illegal!");
                }

                for(j=start; j <= end; j++)
                {
                        ports[i++] = j;
                }

  
                expr = p + 1;
        }
        if (*expr == '-')
         {
                start = 1;
                end = atoi(*(expr+ 1));
        }
        else
         {
                start = end = atoi(expr);
                if ((q =  strchr(expr,'-')) && *(q+1) ) end = atoi(q+1);
                else if (q && !*(q+1))
                 {
                         end = 65535;
                 }
        }
    if (1)
    {
        printf("The first port is %d, and the last one is %d\\n", start, end);
    }

    if (start < 1 || start > end)
    {
        fatal("Your port specifications are illegal!");
    }
    for(j=start; j <= end; j++)
    {

        ports[i++] = j;
    }

    ports[i++] = 0;

    tmp = realloc(ports, i * sizeof(short));
    free(mem);
    return tmp;
}
int main(int argc,char ** argv)
{
        int* tmp = getpts(argv[1]);
        FILE *fp;
        fp = fopen("port","wr");

        short int _i;
				// 具体可以完成什么大家就自行发挥啦
}
# 系统安全
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录