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

翻譯作品之五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 <- Router-A ->------------------??????? |--------------------|
????????? 172.16.14.132/30 /????????? | 172.16.1.0/24
?????? /???????? |
172.16.14.64/27 <- Router-B ->--------------------------<- Router-X ->
?????????????????????????????? 172.16.14.136/30? /???????? |
?????????????????????????? /????????? | 172.16.2.0/24
172.16.14.96/27 <- Router-C ->-----------------/??????? |--------------------|
????????? 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 << 24
---------
10101100 00000000 0000000 00000000

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

14 << 8
--------
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) < 1)
??? {
?fprintf(stderr, "Not a standard IPv4 IP address./n");
?exit(1);
??? }
??? return((octet1 << 24) | (octet2 << 16) | (octet3 << 8) | octet4);
}
---------------------------------------------------------------------------

如果你懂得位移的例子,這個函數應該就象塊蛋糕罷了。函數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) < 1)
?{
??fprintf(stderr, "Not a standard IPv4 IP address./n");
??exit(1);
?}
?return((octet1 << 24) | (octet2 << 16) | (octet3 << 8) | octet4);
}
---------------------------------------------------------------------------


]計算掩碼偏移量

在這個例子的子網,/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 < 0) /* check for valid values */
?{
??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 <= end; 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) < 1)
??? {
?fprintf(stderr, "Not a standard IPv4 IP address./n");
?exit(1);
??? }
??? return((octet1 << 24) | (octet2 << 16) | (octet3 << 8) | octet4);
}

unsigned long add_masking(const char *t_addr, int mask)
{
??? if(mask > 32 || mask < 0) /* check for valid values */
??? {
?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 <= end; 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 <= end_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) < 1)
??? {
?fprintf(stderr, "Not a standard IPv4 IP address./n");
?exit(1);
??? }
??? return((octet1 << 24) | (octet2 << 16) | (octet3 << 8) | octet4);
}

unsigned long add_masking(const char *t_addr, int mask)
{
??? if(mask > 32 || mask < 0) /* check for valid values */
??? {
?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;
}
--------------------------------------------------------------------------


好極了,不過這段代碼無可否認並不好,但是可以很好的運行。當作一個例子,你可以整理和使的它更好和結構化。但是在下一篇文章我不管如何都會做的,當我介紹多線程的時候。到是在說!

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