📄 439.htm
字号:
}; <br>
The first element in the structure "sa_family" will be used to <br>
reference what the family <br>
type is for the socket, in our sniffer it will be AF_INET. Next the <br>
"sa_data" element <br>
holds the destination port and address for the socket. To make it <br>
easier to deal with the <br>
sockaddr struct the use of the sockaddr_in structure is commonly <br>
used. Sockaddr_in <br>
makes it easier to reference all of the elements that are contained <br>
by sockaddr. <br>
by sockaddr. <br>
Sockaddr_in looks like: <br>
struct sockaddr_in { <br>
short int sin_family; /* Address family <br>
*/ <br>
unsigned short int sin_port; /* Port number <br>
*/ <br>
struct in_addr sin_addr; /* Internet address <br>
*/ <br>
unsigned char sin_zero[8]; /* Same size as struct <br>
sockaddr */ <br>
}; <br>
We will use this struct and declare a variable "from" which will <br>
give us the information <br>
on the packet that we will collect from the raw socket. For <br>
instance the var <br>
"from.sin_addr" will give access to the packets source address (in <br>
network byte order). The thing to mention here is that all items in <br>
the sockaddr_in <br>
structure must be in network-byte order. When we receive the data <br>
in the sockaddr_in <br>
struct we must then convert it back to Host-byte order. To do this we <br>
can use some <br>
can use some <br>
predefined functions to convert back and forth between host and <br>
network byteorder. <br>
Here are the functions we will use: <br>
ntohs : this function converts network byte order to host byt <br>
e <br>
order <br>
for a 16-bit short <br>
ntohl : same as above but for a 32-bit long <br>
inet_ntoa : this function converts a 32-bit network binary value t <br>
o <br>
a <br>
dotted decimal ip address <br>
inet_aton : converts a character string address to the 32-bit <br>
network <br>
binary value <br>
inet_addr : takes a char string dotted decimal addr and returns a <br>
32-bit <br>
network binary value <br>
To further illustrate ,say I want to know the port number that this <br>
packet originated from: <br>
int packet_port; packet_port =ntohs(from.sin_port); <br>
^^^^^ <br>
If I want the source IP address of the packet we will use a special <br>
function to get it to the <br>
123.123.123.123 format: <br>
char *ip_addr; ip_addr =inet_ntoa(from.sin_addr) <br>
^^^^^^^^^ <br>
line 11-12: <br>
struct ip *ip : <br>
struct tcp *tcp : <br>
This is a structure that we defined in our header file "headers. <br>
h". This structure is <br>
declared so that we can access individual fields of the ip/tcp <br>
header. The structure is like <br>
a transparent slide with predefined fields drawn on it. When a packet <br>
is taken off <br>
the wire it is a stream of bits, to make sense of it the <br>
"transparency" (or cast) is laid on <br>
top of or over the bits so the individual fields can be referenced. <br>
Line 14 : <br>
sock = socket(AF_INET, SOCK_RAW, IPPROTO_TCP); <br>
This is the most important line in the entire program. Socket() takes <br>
three arguments in <br>
this form: <br>
this form: <br>
sockfd = socket(int family, int type, int protocol); <br>
The first argument is the family. This could be either AF_UNIX which <br>
is used so a process <br>
can communicate with another process on the same host or AF_INET <br>
which is used for <br>
internet communication between remote hosts. In this case it will be <br>
AF_INET . Next <br>
is the type, the type is usually between 1 of 4 choices (there are <br>
others that we will not <br>
discuss here) the main four are : <br>
1. SOCK_DRAM : used for udp datagrams <br>
2. SOCK_STREAM : used for tcp packets <br>
3. SOCK_RAW : used to bypass the transport layer <br>
and directly access the IP layer <br>
4. SOCK_PACKET : this is linux specific, it is similuar to <br>
SOCK_RAW except it accesses the DATA LINK <br>
Layer <br>
For our needs we will use the SOCK_RAW type. You must have root <br>
acces to open a <br>
raw socket. The last parameter is the protocol,the protocol value <br>
specifies what type of <br>
traffic the socket should receive , for normal sockets this value is <br>
usally set to "0" <br>
because the socket can figure out if for instance the "type" of <br>
SOCK_DGRAM is <br>
specified then the protocol should be UDP.In our case we just want <br>
to look at tcp <br>
traffic so we will specify IPPROTO_TCP. <br>
line 15 : <br>
while (1) <br>
The while (1) puts the program into an infinite loop this is necessary <br>
so that after the <br>
first packet is processed we will loop around and grab the next. <br>
Line 18: <br>
bytes_recieved = recvfrom(sock, buffer, sizeof buffer, 0, (struct <br>
sockaddr *)&from, &fromlen); <br>
Now here is where we are actually reading data from the open socket <br>
"sock".The from <br>
struct is also filled in but notice that we are casting "from" from a <br>
"sockaddr_in" struct <br>
to a "sockaddr" struct. We do this because the recvfrom() requires <br>
a sockaddr type but <br>
to access the separate fields we will continue to use the sockaddr_in <br>
structure. The <br>
length of the "from" struct must also be present and passed by <br>
address. The recvfrom() <br>
call will return the number of bytes on success and a -1 on error and <br>
fill the global var <br>
errno. <br>
This is what we call "blocking-I/O" the recvfrom() will wait here <br>
forever until a <br>
datagram on the open socket is ready to be processed. This is opposed <br>
to <br>
Non-blocking I/O which is like running a process in the background <br>
and move on to <br>
other tasks. <br>
Line 20: <br>
printf("Source address ::: %s\n",inet_ntoa(from.sin_addr)); <br>
This printf uses the special function inet_ntoa() to take the value <br>
of "from.sin_addr" <br>
which is stored in Network-byte order and outputs a value in a <br>
readable ip form such <br>
as 192.168.1.XXX. <br>
Line 21: <br>
ip = (struct ip *)buffer; <br>
This is where we will overlay a predefined structure that will help <br>
us to individually <br>
identify the fields in the packet that we pick up from the open <br>
socket. <br>
Line 22: <br>
printf("IP header length ::: %d\n",ip->ip_length); <br>
The thing to notice on this line is the "ip->ip_length" this will <br>
access a pointer in <br>
memory to the ip header length the important thing to remember is <br>
that the length <br>
will be represented in 4-byte words this will be more important later <br>
when trying to <br>
access items past the ip header such as the tcp header or the data <br>
portion of the packet. <br>
Line 23: <br>
printf("Protocol ::: %d\n",ip->ip_protocol); <br>
This gives access to the type of protocol such as 6 for tcp or 17 for <br>
udp. <br>
Line 24: <br>
tcp = (struct tcp *)(buffer + (4*ip->ip_length)); <br>
Remember earlier it was mentioned that the ip header length is <br>
stored in 4 byte words, <br>
this is where that bit of information becomes important. Here we <br>
are trying to get access <br>
to the tcp header fields, to do this we must overlay a structure that <br>
has the fields <br>
predefined just as we did with ip. There is one key difference here <br>
the ip header fields <br>
were easy to access due to the fact that the beginning of the <br>
buffer was also the beginning <br>
of the ip header as so : <br>
|----------------- buffer ----------------| <br>
_________________________________________ <br>
| ip header | | <br>
|____________________|____________________| <br>
^ <br>
*ip <br>
^ <br>
*buffer <br>
So to get access to the ip header we just set a pointer casted <br>
as an ip structure to the <br>
beginning of the buffer like "ip = (struct ip *)buffer;". To get <br>
access to the tcp header <br>
is a little more difficult due to the fact that we must set a pointer <br>
and cast it as a tcp <br>
structure at the beginning of the tcp header which follows the ip <br>
header in the buffer <br>
as so : <br>
|----------------- buffer ---------------| <br>
________________________________________ <br>
| ip header | tcp header | | <br>
|___________|____________|_______________| <br>
^ <br>
*tcp <br>
This is why we use 4*ip->ip_length to find the start of the tcp header. <br>
Line 25-26: <br>
printf("Source port ::: %d\n",ntohs(tcp->tcp_source_port); <br>
printf("Dest port ::: %d\n",ntohs(tcp->tcp_dest_port)); <br>
We can now access the source and dest ports which are located in <br>
the tcp header via <br>
the structure as defined above. <br>
This will conclude our first very simple tcp sniffer. This was <br>
a very basic application <br>
that should help define how to access packets passing on the network <br>
and how to use <br>
sockets to access the packets. Hopefully this will be the first of <br>
many papers to come, <br>
which each proceeding paper we will add a new or more complex feature <br>
to the sniffer. I <br>
should also mention that there a number of great resources on the net <br>
that should aid you <br>
in further research in this area : <br>
1. Beej's Guide to Network Programming <br>
This is an awesome paper that really helps <br>
clear up any misconceptions about network programming. <br>
[http://www.ecst.csuchico.edu/~beej/guide/net] <br>
2. TCP/IP Illustrated Vol 1,2,3 <br>
W.Richard Stevens <br>
To use the above program, cut out the above code and strip off all <br>
of the line numbers. Save the edited file as sniff.c. Next cut <br>
out the header file headers.h (below) and save it to a file headers.h <br>
in the same directory. Now just compile: gcc -o sniff sniff.c <br>
You should now have the executable "sniff", to run it type <br>
#./sniff <br>
/*************************headers.h**************************/ <br>
/*structure of an ip header */ <br>
struct ip { <br>
unsigned int ip_length:4; /*little-endian*/ <br>
unsigned int ip_version:4; <br>
unsigned char ip_tos; <br>
unsigned short ip_total_length; <br>
unsigned short ip_id; <br>
unsigned short ip_flags; <br>
unsigned char ip_ttl; <br>
unsigned char ip_protocol; <br>
unsigned short ip_cksum; <br>
unsigned int ip_source; <br>
unsigned int ip_dest; <br>
}; <br>
/* Structure of a TCP header */ <br>
struct tcp { <br>
unsigned short tcp_source_port; <br>
unsigned short tcp_dest_port; <br>
unsigned int tcp_seqno; <br>
unsigned int tcp_ackno; <br>
unsigned int tcp_res1:4, /*little-endian*/ <br>
tcp_hlen:4, <br>
tcp_fin:1, <br>
tcp_syn:1, <br>
tcp_rst:1, <br>
tcp_psh:1, <br>
tcp_ack:1, <br>
tcp_urg:1, <br>
tcp_res2:2; <br>
unsigned short tcp_winsize; <br>
unsigned short tcp_cksum; <br>
unsigned short tcp_urgent; <br>
}; <br>
/*********************EOF***********************************/ <br>
</small><hr>
<p align="center">[<a href="index.htm">回到开始</a>][<a href="317.htm">上一层</a>][<a href="440.htm">下一篇</a>]
<p align="center"><a href="http://cterm.163.net">欢迎访问Cterm主页</a></p>
</table>
</body>
</html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -