现在的位置: 首页 > 综合 > 正文

翻译作品之五Coding a TCP Connect Port Scanner: Using VLSM rev 1.0

2013年12月08日 ⁄ 综合 ⁄ 共 11756字 ⁄ 字号 评论关闭

翻译作品之五Coding a TCP Connect Port Scanner: Using VLSM rev 1.0
???? ---nightcat

这篇文章假设读者已经理解了在这个系列的前面两篇文章,或者已经有了c基础和bsd套接字编程技能。同时也假设理解了ip地址和子网的知识。

----------------------------------------------------------------------
******译者补充子网划分的方法,希望可以加深大家的理解:*****

?

IP地址规划时总是很头疼子网和掩码的计算:
题目1:
一个主机的IP地址是202.112.14.137,掩码是255.255.255.224,要求计算这个主机所在网络的网络地址和广播地址。

解决方法:
  常规办法是把这个主机地址和子网掩码都换算成二进制数,两者进行逻辑与运算后即可得到网络地址。其实大家只要仔细想想,可以得到另一个方法:255.255.255.224的掩码所容纳的IP地址有256-224=32个(包括网络地址和广播地址),那么具有这种掩码的网络地址一定是32的倍数。而网络地址是子网IP地址的开始,广播地址是结束,可使用的主机地址在这个范围内,因此略小于137而又是32的倍数的只有128,所以得出网络地址是202.112.14.128。而广播地址就是下一个网络的网络地址减1。而下一个32的倍数是160,因此可以得到广播地址为202.112.14.159

?

题目2:
? 根据每个网络的主机数量进行子网地址的规划和计算子网掩码。这也可按上述原则进行计算。

比如一个子网有10台主机,那么对于这个子网就需要10+1+1+1=13个IP地址。(注意加的第一个1是指这个网络连接时所需的网关地址,接着的两个1分别是指网络地址和广播地址。)13小于16(16等于2的4次方),所以主机位为4位。而256-16=240,所以该子网掩码为255.255.255.240。

  如果一个子网有14台主机,不少同学常犯的错误是:依然分配具有16个地址空间的子网,而忘记了给网关分配地址。这样就错误了,因为14+1+1+1=17 ,大于16,所以我们只能分配具有32个地址(32等于2的5次方)空间的子网。这时子网掩码为:255.255.255.224。

a类子网分类例子:
192.168.1.1/12

每个子网有2^(32-12)台主机

有2^(16-12)个b网段
有2^(24-12)个c网段

所有的网段共有2^(16-12)*2^(24-12)*256-2 台主机

?

b类子网分类例子:
192.168.1.1/19

每个子网有2^(32-19)台主机

有2^(24-19)个c网段

所有的网段共有2^(24-19)*256-2 台主机

?

c类子网划分方法:

192.168.1.1/27

每个子网有2^(32-27)台主机

共有2^(32-27)*256-2 台主机

希望上面的说明可以让你理解怎么划分子网和怎么计算机子网。
————————————————————————————————————
]介绍

大部分的端口和漏洞扫描器假设了使用者只是想扫描一个子网,虽然虽然它会工作很长时间,不过可以稍微限制的。如果攻击者从探测的状况显示目标主机用着变长子网掩码(VLSM)和无类域间路由(CIDR),那么准备那些子网的列表就稍微有点费力。

在这篇文章我稍微不足地合并了变长子网掩码和无类域间路由在一个网络扫描器里。我已经选择b类地址172.16.0.0 作为一个例子工作,来贯穿整篇文章。

? 在搜索了存在的代码和运算法则来学习之后,我最后来到ipsc.sourgeforge.net。这是我能找到的唯一的用c写的计算子网方法。有很多的都是用jacascript来写的,但是那些运算法则都不见得好。我有点相信这篇文章是网上这类文章的第一篇,如果不是请联系我。我很想去看看其它的解决方法。

有时我的解说可能有点难于理解,但是我有信心把问题分成几个部分同时细心地分析它们。

?

]变长子网掩码 复习

让我们假设目标网络,inc。一个很大的isp,已经分配了172.16.0.0/16。我们将集中他们的四个路由器。Router-X 直接路由, Router-A, Router-B, and Router-C 是通过点对点的帧中继连接到Router-X

这里是简单的图形化的表示:

172.16.14.32/27 ------------------??????? |--------------------|
????????? 172.16.14.132/30 /????????? | 172.16.1.0/24
?????? /???????? |
172.16.14.64/27 --------------------------
?????????????????????????????? 172.16.14.136/30? /???????? |
?????????????????????????? /????????? | 172.16.2.0/24
172.16.14.96/27 -----------------/??????? |--------------------|
????????? 172.16.14.140/30

一个无知的攻击者,可能指定他们的工具去扫描172.16.0.0/16 ,这整个b类的网络。那么可供选择的方法是什么呢 ?

我们可以合并一个程序去扫描从一个扩展的文件里得到的ip地址。(See paper #2 of this series) 这些ip可以很容易的得到从一个现存的端口扫描器象nmap:

[modular@truncode]$ nmap -sL? -n 172.16.14.32/27 | grep '^Host' | /
cut -d '(' -f 2 | cut -d')' -f 1 > target-ips.txt

这会给出我们172.16.14.32-63之间的ip地址列表。到那时我们可以输入列表到我们的扫描器。方法不错,不过有点难使用。

稍后我们想一个自己的扫描器,它能容易地自动执行某个任务。

?

]strtok() 妙用

这个tcp端口扫描器应该能够取得一个参数172.16.14.32/27.
例如:
?用'/'作为分隔附分离成两个变量,我将调用这些变量,网络号和掩码。为了分离argv[1]成为单独的标志,我们用便利的strtok函数(man 3 strtok显示如下的原型):
---------------------------------------------------------------------------
#include

?????? char *strtok(char *s, const char *delim);
??????
---------------------------------------------------------------------------

这个man 页解说,一个标志是一个字符串,但并不都是匹配'delim'字符串。在这种情况下'delim'会用 '/'。我们将需要调用strtok()两次,为了包含'/' 之前和之后的字符串。第二次调用strtok() 应该设置第一个参数为NULL,因为每次调用返回一个指向下一个标记的指针和到那时当没有更多的标记是用NULL,我的说明总是逐字从man页分离出来的。

如下的小程序作为怎样在扫描器中用strtok()的例子:

---------------------------------------------------------------------------
#include
#include
#include

int main(int argc, char *argv[])
{
??? char *network, *mask;

??? network = strtok(argv[1], "/");
??? mask = strtok(NULL, "/");

??? /* print out tokens just for the sake
???? * of example.
???? */
??? printf("network = %s/nmask = %s/n", network, mask);

??? return 0;
}
---------------------------------------------------------------------------

编译这个例子和运行它:
[modular@truncode]$ gcc strtok_ex.c -o strtok_ex
[modular@truncode]$ ./strtok_ex 172.16.14.32/27
network = 172.16.14.32
mask = 27

目前一切很好,我们把注意力集中到位移的技术。

]位操作

在开始运算前必须把目标网络的ip地址换成整数。把每个十进制的换成二进制整数,使的它更加容易理解:
172 = 10101100
16? = 10000
14? = 1110
32? = 100000

想象一下 24 个0 作为占位符(思考ip地址-4个十进制)。可视化更能帮助理解。每个十进制将需要移动到它适合位置:

172 ---------
10101100 00000000 0000000 00000000

16 --------
00000000 00010000 0000000 00000000

14 --------
00000000 00000000 0001110 00000000

32
--------
00000000 00000000 0000000 00100000

现在加在一起:
172 = 10101100 00000000 0000000 00000000
16? = 00000000 00010000 0000000 00000000
14? = 00000000 00000000 0001110 00000000
32? = 00000000 00000000 0000000 00100000
----------------------------------------
????? 10101100 00010000 0001110 00100000 = 2886733344

现在有的这个-- 一个整数表示一个点号分隔的十进制ip地址。

你可能立刻注意到这个整数是十分大。一个无符号长整型有一个变量范围从0 to 4,294,967,295 这个数应该足够大了。

一个函数被写基于先前的例子:

---------------------------------------------------------------------------
unsigned long convert_ip(const char *t_addr)
{
??? int octet1 = 0;
??? int octet2 = 0;
??? int octet3 = 0;
??? int octet4 = 0;
??? if(sscanf(t_addr, "%d.%d.%d.%d",
??&octet1, &octet2, &octet3, &octet4) ??? {
?fprintf(stderr, "Not a standard IPv4 IP address./n");
?exit(1);
??? }
??? return((octet1 }
---------------------------------------------------------------------------

如果你懂得位移的例子,这个函数应该就象块蛋糕罢了。函数sscanf()期待"%d.%d.%d.%d"的格式在字符串变量t_addr中 和分离每个整数成为新的变量。到那时我们用

增加这个新的convert_ip()函数到第一个例子程序中:

---------------------------------------------------------------------------
#include
#include
#include

unsigned long convert_ip(const char *t_addr);

int main(int argc, char *argv[])
{
?char *network, *mask;
?network = strtok(argv[1], "/");
?mask = strtok(NULL, "/");
?/* print out tokens just for the sake
? * of example.
? */
?printf("network = %s/nmask = %s/n", network, mask);
?printf("Our transformed IP address %s is %lu/n",
???argv[1], convert_ip(argv[1]));
?return 0;
}

unsigned long convert_ip(const char *t_addr)
{
?int octet1 = 0;
?int octet2 = 0;
?int octet3 = 0;
?int octet4 = 0;
?
?if(sscanf(t_addr, "%d.%d.%d.%d",
????&octet1, &octet2, &octet3, &octet4) ?{
??fprintf(stderr, "Not a standard IPv4 IP address./n");
??exit(1);
?}
?return((octet1 }
---------------------------------------------------------------------------

]计算掩码偏移量

在这个例子的子网,/27或者255.255.255.224 被选择作为这个子网掩码。这个给了2^5 = 32地址在每个子网中,到那时这个转换ip地址是被增加到32和减去1。让我们回头看看convert_ip()函数给我们在172.16.14.32网络中是什么整数。首先一个形式的整数2886733344和转换成二进制:

10101100 00010000 00001110 00011111

现在加32:
-----------
10101100 00010000 00001110 00011111
00000000 00000000 00000000 00100000
-----------------------------------
10101100 00010000 00001110 00111111

这样你能看到这个刚好给我们32个ip的新地址。我们新的函数会返回这个数作为一个子网的结尾整数:

---------------------------------------------------------------------------
unsigned long add_masking(const char *t_addr, int mask)
{
?if(mask > 32 || mask ?{
??fprintf(stderr, "Not a valid subnet mask./n");
??exit(1);
?}
?return((int)pow(2, 32 - mask) + convert_ip(t_addr) -1);
}
---------------------------------------------------------------------------

?

]把修改的ip地址换成整数

这是我们能写一个完全的VLSM ip 产生程序最后所必须的。这个被用到的运算法则基本上是convert_ip反函数。这次 AND 操作符被用来零化不需要的八位字节。和那时所有的位是被移动到右边。再次看看我们用到的第一个整数。

10101100 00010000 0001110 00100000 = 2886733344

10101100 00010000 0001110 00100000
11111111 00000000 0000000 000000000 &
-----------------------------------
10101100 00000000 0000000 000000000 = 2885681152

现在移动这个整数到右边....

2885681152 >> 24
----------------
00000000 00000000 0000000 10101100 = 172

等等....

这里是一个转化一个整数为点分的十进制ip地址的函数:

---------------------------------------------------------------------------
char *reverse_int(unsigned long addr)
{
?static char *buffer[BUF_SIZE];
?snprintf((char *)buffer, BUF_SIZE, "%d.%d.%d.%d",
???(addr & 0xff000000) >> 24,
???(addr & 0x00ff0000) >> 16,
???(addr & 0x0000ff00) >> 8,
???(addr & 0x000000ff));
?return((char *) buffer);
}
---------------------------------------------------------------------------

snprintf()函数用格式"%d.%d.%d.%d"和写入字符串'buffer'。任何一个熟悉在tcpdump中写BPF过滤器应该认识AND 和 0xff的逻辑,在reverse_int()函数中。

]一个例程:

---------------------------------------------------------------------------
#include
#include
#include
#include
#define BUF_SIZE 128

unsigned long convert_ip(const char *t_addr);
unsigned long add_masking(const char *t_addr, int mask);
char *reverse_int(unsigned long addr);

int main(int argc, char *argv[])
{
??? char *network, *mask;
??? unsigned long start = 0, end = 0, current = 0;

??? /* separate network and mask into two
???? * usable tokens
???? */
??? network = strtok(argv[1], "/");
??? mask = strtok(NULL, "/");

??? start = convert_ip(network);
??? end = add_masking(network, atoi(mask));

??? for(current = start; current ??? {
?printf("%s/n", reverse_int(current));
??? }

??? return 0;
}

unsigned long convert_ip(const char *t_addr)
{
??? int octet1 = 0;
??? int octet2 = 0;
??? int octet3 = 0;
??? int octet4 = 0;

??? if(sscanf(t_addr, "%d.%d.%d.%d",
??&octet1, &octet2, &octet3, &octet4) ??? {
?fprintf(stderr, "Not a standard IPv4 IP address./n");
?exit(1);
??? }
??? return((octet1 }

unsigned long add_masking(const char *t_addr, int mask)
{
??? if(mask > 32 || mask ??? {
?fprintf(stderr, "Not a valid subnet mask./n");
?exit(1);
??? }
??? return((int)pow(2, 32 - mask) + convert_ip(t_addr) -1);
}

char *reverse_int(unsigned long addr)
{
??? static char *buffer[BUF_SIZE];
??? snprintf((char *)buffer, BUF_SIZE, "%d.%d.%d.%d",
???? (addr & 0xff000000) >> 24,
???? (addr & 0x00ff0000)?>> 16,
???? (addr & 0x0000ff00) >> 8,?
???? (addr & 0x000000ff));
??? return((char *) buffer);
}
---------------------------------------------------------------------------

确保不要忘记编译是带有manth库。这个是用pow()所必要的。

[modular@visioncode]$ gcc -o ipgen ipgen.c -lm
[modular@visioncode]$ ./ipgen 172.16.14.32/27
172.16.14.32
172.16.14.33
172.16.14.34
172.16.14.35
172.16.14.36
172.16.14.37
172.16.14.38
172.16.14.39
172.16.14.40
172.16.14.41
172.16.14.42
172.16.14.43
172.16.14.44
172.16.14.45
172.16.14.46
172.16.14.47
172.16.14.48
172.16.14.49
172.16.14.50
172.16.14.51
172.16.14.52
172.16.14.53
172.16.14.54
172.16.14.55
172.16.14.56
172.16.14.57
172.16.14.58
172.16.14.59
172.16.14.60
172.16.14.61

真好,我们已经可以得到目标主机网络172.16.14.32/27的ip地址列表。

?

]在tcp端口连接扫描器用VLSM

现在是到合并所有新学的信息成为有用的东西的时候了,查阅这个系列的第一和第二篇文章理解端口扫描代码。

---------------------------------------------------------------------------
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define TRUE ?1
#define FALSE?0
#define BUF_SIZE 128

unsigned long convert_ip(const char *t_addr);
unsigned long add_masking(const char *t_addr, int mask);
char *reverse_int(unsigned long addr);
int portscan(char *remote_ip, u_short port);

int main(int argc, char *argv[])
{
??? struct hostent *he;
??? struct servent *srvc;
??? struct in_addr t_addr;
??? int start_port, end_port, counter;
??? int i;
????
??? /* variables for vlsm routines */
??? char *network, *mask;
??? unsigned long start = 0, end = 0, current = 0;

??? /* start and end ports */
??? start_port = atoi(argv[2]);
??? end_port = atoi(argv[3]);

??? /* separate network and mask into two
???? * usable tokens
???? */
??? network = strtok(argv[1], "/");
??? mask = strtok(NULL, "/");

??? start = convert_ip(network);
??? end = add_masking(network, atoi(mask));

??? for(current = start; current ??? {
?t_addr.s_addr = inet_addr(reverse_int(current));

?if ((he=gethostbyaddr((char*)&(t_addr.s_addr),
???sizeof(reverse_int(current)),AF_INET)) == NULL) {
???? herror("gethostbyname");
???? continue;
?}
???????
?printf("/n");
?printf("Interesting ports on %s (%s)/n/n",
??he->h_name, reverse_int(current));
?printf("port/tstate/tservice/n");
?
?for(counter = start_port; counter ?{???
???? if((i = portscan(reverse_int(current), counter) == 0))
??continue;
???? else
??srvc = getservbyport(htons(counter), "tcp");
???? ???
???? printf("%d/tcp/topen/t%s/n", counter,
????? (srvc == NULL)?"unknown":srvc->s_name);
?}
??? }
??? return 0;
}

unsigned long convert_ip(const char *t_addr)
{
??? int octet1 = 0;
??? int octet2 = 0;
??? int octet3 = 0;
??? int octet4 = 0;

??? if(sscanf(t_addr, "%d.%d.%d.%d",
??&octet1, &octet2, &octet3, &octet4) ??? {
?fprintf(stderr, "Not a standard IPv4 IP address./n");
?exit(1);
??? }
??? return((octet1 }

unsigned long add_masking(const char *t_addr, int mask)
{
??? if(mask > 32 || mask ??? {
?fprintf(stderr, "Not a valid subnet mask./n");
?exit(1);
??? }
??? return((int)pow(2, 32 - mask) + convert_ip(t_addr) -1);
}

char *reverse_int(unsigned long addr)
{
??? static char *buffer[BUF_SIZE];
??? snprintf((char *)buffer, BUF_SIZE, "%d.%d.%d.%d",
???? (addr & 0xff000000) >> 24,
???? (addr & 0x00ff0000)?>> 16,
???? (addr & 0x0000ff00) >> 8,?
???? (addr & 0x000000ff));
??? return((char *) buffer);
}

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;
}
--------------------------------------------------------------------------

好极了,不过这段代码无可否认并不好,但是可以很好的运行。当作一个例子,你可以整理和使的它更好和结构化。但是在下一篇文章我不管如何都会做的,当我介绍多线程的时候。到是在说!

抱歉!评论已关闭.