Hướng dẫn viết một chương trình Sniffer

Bài này mình hướng dẫn các bạn viết một sniffer (nghe lén) packet đơn giản trên Windows. Để lập trình được thì cần các bạn cần có kiến thức trình socket  với thư viện Winsock trong ngôn ngữ C.

Với cách lập trình socket thông thường chúng ta sử dụng SOCK_STREAM, SOCK_DGRAM tức lập trình ở mức Application của mạng mà thôi, để lập trình ở mức thấp nhất (tức tầng Network trong 4 tầng của Internet: Tầng Network Access –> Tầng Internet –> Tầng Transport –> Tầng Application) sử dụng SOCK_RAW là một dạng socket “thô”.

Điều đầu tiên bao giờ chúng ta cũng phải khởi tạo thư viện Winsock. Hàm khởi tạo thư viện như sau:
Code:

void InitWinSock()
 {
 	WSADATA wsaData;
 	if (WSAStartup(MAKEWORD(1,1),&wsaData)!=0) return;
 }

Chi tiết hàm WSAStartup có thể xem trợ giúp của MSDN … 

Cách tạo một Raw Socket như sau:
Code:

int	CreateRawSocket()
 {
 	int	sock;
 	if ((sock=WSASocket(AF_INET,SOCK_RAW,0,NULL,NULL,WSA_FLAG_OVERLAPPED))
                    ==INVALID_SOCKET) return 0;
 	return sock;
 }

Hàm trên trả về một socket mới nếu như không gặp lỗi.

Như chúng ta đã biết mô hình mạng Internet được phân chia thành 4 tầng (Tầng Network Access <–> Tầng Internet <–> Tầng Transport <–> Tầng Application) tương ứng với 7 tầng của mô hình OSI (Tầng Physical <–> Tầng Data Link <–> Tầng Network <–> Tầng Transport <–> Tầng Session <—> Tầng Presentation <–> Tầng Application). Mình không phải nhắc lại mô hình OSI như thế nào vì có rất nhiều bài viết đã nói đến OSI.

Giới thiệu cấu trúc của gói IP, TCP, ICMP

1. Gói IP:

Định nghĩa một struct mô tả gói IP.
Code:

struct ip_hdr{
 	unsigned	char	ver_ihl; //<-- Version (4 bits)
                                         // + Internet Header Length (4 bits)
 	unsigned	char	tos;	 //<-- Type Of Service
 	unsigned short	tol_len	         //<-- Total Length 
                                         // (Ip Header + TCP Header + Data)
 	unsigned	short	id	 //<-- Identification
 	unsigned	short	offset	 //<-- Frame Offset trong đó
                                         // 8 bits cho flags
 	unsigned	char	ttl	 //<-- Time to Live
 	unsigned char	protocol	 //<-- Protocol
 	unsigned short	checksum         //<-- Header Checksum
 	unsigned int	saddr	         //<-- Source Address
 	unsigned	int	daddr	 //<-- Destination Address
 };

2. Gói TCP:

Định nghĩa một struct mô tả gói TCP.
Code:

struct tcp_hdr{
 	unsigned	short	sport	  //<-- Source Port
 	unsigned short	dport	          //<-- Destiantion Port
 	unsigned int	seq_num	          //<-- Sequence Number
 	unsigned	int	ack_num	  //<-- Acknowledgment Number
 	unsigned char	dataoffset        //<--Data Offset
 	unsigned char	flags	          //Flags: SYN, ACK, FIN, RST, PUSH,
                                          // URG (2 bits) + 6 bits Reserve
 	unsigned short	window	          // Windows
 	unsigned short	checksum          // Checksum
 	unsigned short	urgpointer        // Urgent Pointer
};

3. Gói ICMP:

Định nghĩa một struct mô tả gói ICMP
Code:

struct icmp_hdr{
 	unsigned char	type	          // Type
 	unsigned char	code	          // Code
 	unsigned short	checksum
 	unsigned short	id	          // Identification
 	unsigned short	sequence
 	unsigned short	timestamp
};

Ở trên là toàn bộ định nghĩa về các gói IP, ICMP, TCP. Trong bài này mình chỉ hướng dẫn bắt các gói TCP và ICMP mà thôi, còn các gói khác thì có thể làm tương tự.

Code:

#include <winsock2.h>
#include <stdio.h>
#define SIO_RCVALL _WSAIOW(IOC_VENDOR,1)
#pragma comment (lib,"ws2_32.lib")
void	InitWinSock();
int		CreateRawSocket();
int		Sniffer(int	sock);
int		packetcapture(char *packet);
int		tcp_display(struct ip_hdr	*ip,struct tcp_hdr *tcp);
int		icmp_display(struct ip_hdr *ip,struct icmp_hdr *icmp);

struct ip_hdr
{
 	unsigned char	ver_ihl;          // VER va IHL (Internet Header Length)
 	unsigned char	tos;	          // Type of Server
 	unsigned short	tol_len;          // Total Length
                                          // =IpHeader + TCP Header + Data
 	unsigned short	id;		  // Identification
 	unsigned short	offset;	          // Frame offset
 	unsigned char	ttl;	          // Time To Live
 	unsigned char	protocol;
 	unsigned short	checksum;
 	unsigned int	saddr;	          // Source Address
 	unsigned int	daddr;	          // Destination Address
};

struct tcp_hdr
{
	unsigned short	sport;	          // Source Port
 	unsigned short	dport;	          // Detinations Port	
 	unsigned int	seqnum;	          // Sequence;
 	unsigned int	acknum;	          // Acknowlege
 	unsigned char	dataoffset;	  // Data Offset
 	unsigned char	flags;	          // SYN, URG, FIN, ACK, 
                                          // RST, PUSH + Preserve
 	unsigned short  windows;
 	unsigned short	checksum;
 	unsigned short	urgpointer;
};

struct icmp_hdr
{
 	unsigned char	type;
 	unsigned char	code;
 	unsigned short	checksum;
 	unsigned short	id;
 	unsigned short	sequence;
 	unsigned short	timestamp;
};

int	socksniffer;

int	main()
{
 	InitWinSock();
 	socksniffer=CreateRawSocket();
 	printf("Seamoun (http://nhomvicki.net)\n");
 	printf("Sniffer TCP, ICMP v1.0\n\n");
 	printf("          Packet Capture ...\n");
 	Sniffer(socksniffer);
 	return 0;
}

//Ham khoi tao thu vien WinSock
void InitWinSock()
{
 	WSADATA wsaData;
 	if (WSAStartup(MAKEWORD(1,1),&wsaData)!=0) return;
}

//Ham tao RawSocket
int	CreateRawSocket()
{
 	int	sock;
 	if ((sock=WSASocket(AF_INET,SOCK_RAW,0,NULL,NULL,WSA_FLAG_OVERLAPPED))                                                        ==INVALID_SOCKET) return 0;
 	return sock;
}

//Ham thuc hien Sniffer
int	Sniffer(int sock)
{
 	DWORD dwBufferLen[10], dwBufferInLen = 1, dwBytesReturned = 0;
 	char *HostName=new char [32];
 	unsigned long	nSize=32;
 	struct	hostent	*hp;
 	sockaddr_in	from,dest;
 	int	sread,fromlen=sizeof(from);
 	char *packet=new char [2048];
 	if (GetComputerName(HostName,&nSize)==0) return -1;
 	if ((hp=gethostbyname(HostName))==0) return -1;
 	delete(HostName);
 	int	i=0;
 	while ((hp->h_addr_list[i+1])!=NULL)
 	{
 		i++;
 	}
 	memcpy(&from.sin_addr.s_addr,hp->h_addr_list[i],hp->h_length);
 	if ((hp=gethostbyname(inet_ntoa(from.sin_addr)))==NULL) return -1;
 	memset(&dest,0,sizeof(dest));
	memcpy(&dest.sin_addr.s_addr,hp->h_addr_list[0],hp->h_length);
 	dest.sin_family=AF_INET;
 	dest.sin_port=htons(8000);
 	if (bind(sock,(struct sockaddr *)&dest,sizeof(dest))==SOCKET_ERROR)
                 return -1;

 	//WSAIoctl: dieu khien vao ra socket
 	if(WSAIoctl(sock, SIO_RCVALL, &dwBufferInLen, sizeof(dwBufferInLen),
                     &dwBufferLen, sizeof(dwBufferLen),
                     &dwBytesReturned, NULL, NULL) == SOCKET_ERROR)
         return(-1);

 	//Don nhan va su ly goi du lieu
 	while (1)
 	{
 		sread=recvfrom(sock,packet,8191,0,
                                (struct sockaddr *)&from.sin_addr, &fromlen);
 		if ((sread==SOCKET_ERROR)||sread<0) continue ;
 		packetcapture(packet);
 	}
 	delete(packet);
 	return 0;
}

//Ham thuc hien phan tich goi du lieu
int	packetcapture(char *packet)
{
 	struct ip_hdr	*ip;
 	struct tcp_hdr	*tcp;
 	struct icmp_hdr	*icmp;
 	ip=(struct ip_hdr *)packet;
 	tcp=(struct tcp_hdr *)(packet+sizeof(struct ip_hdr));
 	icmp=(struct icmp_hdr *)(packet+sizeof(struct ip_hdr));
 	switch(ip->protocol)
 	{
 	case 6:
 		tcp_display(ip,tcp);
 		break;
 	case 1:
 		icmp_display(ip,icmp);
 		break;
 	default:
 		break;
 	}
 	return 0;
}

//Ham hien thi goi TCP
int	tcp_display(ip_hdr *ip,tcp_hdr *tcp)
{
 	printf("TCP: [%d] %s:%d", ntohs(ip->tol_len), 
                 inet_ntoa(*(struct in_addr *)&ip->saddr), ntohs(tcp->sport));
 	printf(" > %s:%d|ttl = %d|win = %d|checksum = %d|flag = %d\n\n", 
                 inet_ntoa(*(struct in_addr *)&ip->daddr), 
 	         ntohs(tcp->dport), ip->ttl, ntohs(tcp->windows), 
                 tcp->checksum, tcp->flags);
 	return 0;
}

//Ham hien thi goi ICMP
int icmp_display(struct ip_hdr *ip, struct icmp_hdr *icmp)
{  	
 	printf("ICMP: [%d] %s", ntohs(ip->tol_len), 
                 inet_ntoa(*(struct in_addr *)&ip->saddr));
 	printf(" > %s type = %d|code = %d|ttl = %d|seq = %x\n", 
                 inet_ntoa(*(struct in_addr *)&ip->daddr), 
                 icmp->type, icmp->code, ip->ttl, ntohs(icmp->sequence));
 	return 0;
}

Kết quả khi chạy chương trình:

+ Thực hiện gửi gói ICMP bằng cách dùng lệnh PING:
Code:

C:\>ping 10.0.0.2
 Reply from 10.0.0.2: bytes=32 time<10ms TTL=64
 Reply from 10.0.0.2: bytes=32 time=15ms TTL=64
 Reply from 10.0.0.2: bytes=32 time=15ms TTL=64
 Reply from 10.0.0.2: bytes=32 time=15ms TTL=64

 Ping statistics for 10.0.0.2:
     Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
 Approximate round trip times in milli-seconds:
     Minimum = 0ms, Maximum =  15ms, Average =  11ms

 Chương trình Sniffer:
Seamoun (http://nhomvicki.net)
Sniffer TCP, ICMP v1.0
           Packet Capture ...
ICMP: [60] 10.0.0.1 > 10.0.0.2 type = 8|code = 0|ttl = 128|seq = 3d00
ICMP: [60] 10.0.0.2 > 10.0.0.1 type = 0|code = 0|ttl = 64|seq = 3d00
ICMP: [60] 10.0.0.1 > 10.0.0.2 type = 8|code = 0|ttl = 128|seq = 3e00
ICMP: [60] 10.0.0.2 > 10.0.0.1 type = 0|code = 0|ttl = 64|seq = 3e00
ICMP: [60] 10.0.0.1 > 10.0.0.2 type = 8|code = 0|ttl = 128|seq = 3f00
ICMP: [60] 10.0.0.2 > 10.0.0.1 type = 0|code = 0|ttl = 64|seq = 3f00
ICMP: [60] 10.0.0.1 > 10.0.0.2 type = 8|code = 0|ttl = 128|seq = 4000
ICMP: [60] 10.0.0.2 > 10.0.0.1 type = 0|code = 0|ttl = 64|seq = 4000

+Thực hiện gửi gói TCP bằng Netcat:
Code:

C:\>nx -vv -n 10.0.0.1
Chương trình Sniffer:
Seamoun (http://nhomvicki.net)
Sniffer TCP, ICMP v1.0
          Packet Capture ...
TCP: [64] 10.0.0.1:80 > 10.0.0.2:1033|ttl = 128|win = 64240|checksum = 55340|flag 
= 18
TCP: [60] 10.0.0.2:1033 > 10.0.0.1:80|ttl = 64|win = 5840|checksum = 5942|flag 
= 2
TCP: [52] 10.0.0.2:1033 > 10.0.0.1:80|ttl = 64|win = 5840|checksum = 23589|flag 
= 16

Advertisements

About Argron Nguyen's Blog

Vietnamese. Photographer. Writer. Illustrator. IT-er. All to some extent.

Posted on 06.09.2012, in Network Programming and tagged , , . Bookmark the permalink. Bạn nghĩ gì về bài viết này?.

Trả lời

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Đăng xuất / Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Đăng xuất / Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Đăng xuất / Thay đổi )

Google+ photo

Bạn đang bình luận bằng tài khoản Google+ Đăng xuất / Thay đổi )

Connecting to %s

%d bloggers like this: