Programming with PCAP

zhaozj2021-02-16  43

Because of the recent application of the package sniffing, the younger brother recently contained some information. The following can be described as entry-level classics. Translate it as follows, the level is limited, the error is inevitable, for reference only.

Programming with PCAP

Tim Carstenstimcarst At Yahoo Dot COM

The Latest Version of this Document Can Be Found At http://broker.dhs.org/pcap.htm

Ok, lets begin by defining who this document is written for Obviously, some basic knowledge of C is required, unless you only wish to know the basic theory You do not need to be a code ninja;.. For the areas likely to be understood only by more experienced programmers, I'll be sure to describe concepts in greater detail. Additionally, some basic understanding of networking might help, given that this is a packet sniffer and all. All of the code examples presented here have been tested on FreeBSD 4.3 with a default kernel.Getting Started: The format of a pcap applicationThe first thing to understand is the general layout of a pcap sniffer The flow of code is as follows:.. 1 We begin by determining which interface we want to sniff on. IN Linux this May Beething Like Eth0, in BSD IT May BE XL1, ETC. We can Either Define this Device In A String, or We can as pcap to provide us with the name of an interface tria Will do the job. 2. Initialize Pcap. This is where we actually tell pcap what device we are sniffing on. We can, if we want to, sniff on multiple devices. How do we differentiate between them? Using file handles. Just like opening a file for reading or writing, we must name our sniffing "session" So We can Tell IT Apart from Other Such Sessions. 3. in The Event That We Only Want To Sniff Specific Traffic (EG: Only TCP / IP Packets, Only Packets Going to Port 23, ETC) We Must Create a Rule Set, " Compile "IT, And Apply It. this is a three is close. The rule set is close. The rule set is keepimat That Pcap can read (Hence Compiling it) the compiLIN Actually Just Done by Calling a Function Withninur

it does not involve the use of an external application. Then we tell pcap to apply it to whichever session we wish for it to filter. 4. Finally, we tell pcap to enter it's primary execution loop. In this state, pcap waits until it has received however many packets we want it to Every time it gets a new packet in, it calls another function that we have already defined The function that it calls can do anything we want;.. it can dissect the packet and print it to the User, IT CAN SAVE IN A File, or it can do notning at all. 5. after our Sniffene Contament. This is is is instually a very Simple process. Five Steps Total, ONE of Which is optional (step 3, incase you were wondering.) Why do not we take a look at each of the steps and how to implement them.Setting the deviseThis is terribly simple. There are two techniques for setting the device that we wish to Sniff on.The First Is That We CAN Simply Have The User Tell US. Consider The FOL Lowing Program: #include #include int main (int Argc, char * argv []) {char * dev = argv [1]; printf ("device:% s / n", dev); return (0);} The user specifies the device by passing the name of it as the first argument to the program Now the string "dev" holds the name of the interface that we will sniff on in a format that pcap. CAN Understand (Assuming, Of Course, The User Gave USA Real Interface) .the Other Technique Is Equally Simply. Look at this program: #include #include int main () {char * DEV, Errbuf [PCAP_ERRBUF_SIZE]; DEV = PCAP_LOOKUPDEV (Errbuf); Printf ("Device:% S / N", DEV); return (0);

In This case, PCAP Just Sets The Device on Its Own. "" What is the deal with the errbuf string? "Most of the PCAP Commands Allow US to Pass The String as an argument. The purpose of this string? in the event that the command fails, it will populate the string with a description of the error. in this case, if pcap_lookupdev () fails, it will store an error message in errbuf. Nifty, is not it? And that's how we set our device.Opening the device for sniffingThe task of creating a sniffing session is really quite simple. for this, we use pcap_open_live (). The prototype of this function (from the pcap man page) is as follows :. pcap_t * pcap_open_live (char * device, int snaplen, int promisc, int to_ms, char * ebuf) The first argument is the device that we specified in the previous section snaplen is an integer which defines the maximum number of bytes to be captured By PCAP. Promisc, When Set to True, Brings The Interface Into Promiscuous Mode (However, Even i f it is set to false, it is possible under specific cases for the interface to be in promiscuous mode, anyway) to_ms is the read time out in milliseconds (a value of 0 sniffs until an error occurs;. -1 sniffs indefinitely). lastly, ebuf is a string we can store any error messages within (as we did above with errbuf) The function returns our session handler.To demonstrate, consider this code snippet:. #include ... pcap_t * handle Handle = PCAP_Open_Live (Somedev, Bufsiz, 1, 0, Errbuf); this code fragment opens the devise stiled in the strong "somev"

, Tells it to read however many bytes are specified in BUFSIZ (which is defined in pcap.h). We are telling it to put the device into promiscuous mode, to sniff until an error occurs, and if there is an error, store it in the string errbuf.A note about promiscuous vs. non-promiscuous sniffing: The two techniques are very different in style in standard, non-promiscuous sniffing, a host is sniffing only traffic that is directly related to it only traffic to,.. from, or routed through the host will be picked up by the sniffer. Promiscuous mode, on the other hand, sniffs all traffic on the wire. In a non-switched environment, this could be all network traffic. The obvious advantage to this is That It Provides More Packets for Sniffing, Which May Or May Not Be Helpful Depending On The Reason You Sniffing The Network. However, There area. Promiscuous mode sniffing is detectable;

a host can test with strong reliability to determine if another host is doing promiscuous sniffing. Second, it only works in a non-switched environment (such as a hub, or a switch that is being ARP flooded). Third, on high traffic networks , the host can become quite taxed for system resources.Filtering trafficOften times our sniffer may only be interested in specific traffic. for instance, there may be times when all we want is to sniff on port 23 (telnet) in search of passwords. Or perhaps we want to highjack a file being sent Maybe we only want DNS traffic (port 53 UDP). Whatever the case, rarely do we just want to blindly sniff all network traffic. Enter pcap_compile () and pcap_setfilter over port 21 (FTP). () .The process is quite simple. After we have already called pcap_open_live () and have a working sniffing session, we can apply our filter. Why not just use our own if / else if statements? Two reasons. First, pcap's filter is Far More Efficient, Because IT Does It DirectL y with the BPF filter; we eliminate numerous steps by having the BPF driver do it directly Second, this is a lot easier:..) Before applying our filter, we must "compile" it The filter expression is kept in a regular string ( CHAR ARRAY). The Syntax Is Documented Quite Well in The Man Page for Tcpdump;

I leave you to read it on your own However, we will use simple test expressions, so perhaps you are sharp enough to figure it out from my examples.To compile the program we call pcap_compile () The prototype defines it as:.. Int pcap_compile (pcap_t * p, struct bpf_program * fp, char * str, int optimize, bpf_u_int32 netmask) The first argument is our session handle (pcap_t * handle in our previous example). Following that is a reference to the place we will store the compiled version of our filter. Then comes the expression itself, in regular string format. Next is an integer that decides if the expression should be "optimized" or not (0 is false, 1 is true. Standard stuff.) Finally, we must . specify the net mask of the network the filter applies to The function returns -1 on failure; all other values ​​imply success.After the expression has been compiled, it is time to apply it Enter pcap_setfilter () Following our format of explaining.. PCAP, WE Shall Look At the PCAP_SETFILTER () PROT otype:. int pcap_setfilter (pcap_t * p, struct bpf_program * fp) This is very strait forward The first argument is our session handler, the second is a reference to the compiled version of the expression (presumably the same variable as the second argument to PCAP_COMPILE ()). Perhaps Another Code Sample 10: #include ... pcap_t * handle; / * session handle * / char dev [] = "rl0"; / * Device to Sniff on * / Char errbuf [pcap_errbuf_size]; / * error string * / struct bpf_program filter; / * the compiled filter expression * / char filter_app [] = "port 23";

/ * The filter expression * / bpf_u_int32 mask; / * The netmask of our sniffing device * / bpf_u_int32 net; / * The IP of our sniffing device * / pcap_lookupnet (dev, & net, & mask, errbuf); handle = pcap_open_live (dev, BUFSIZ, 1, 0, errbuf); pcap_compile (handle, & filter, filter_app, 0, net); pcap_setfilter (handle, & filter); This program preps the sniffer to sniff all traffic coming from or going to port 23, in promiscuous mode, on the device rl0.You may notice that the previous example contains a function that we have not yet discussed. pcap_lookupnet () is a function that, given the name of a device, returns its IP and net mask. This was essential because we needed to know the net mask in order to apply the filter. This function is described in the Miscellaneous section at the end of the document.It has been my experience that this filter does not work across all operating systems. in my test environment, I found That OpenBSD 2.9 with a default kernel does support this type of filter, but FreeBSD 4.3 with a default kernel does not. Your mileage may vary.The actual sniffingAt this point we have learned how to define a device, prepare it for sniffing, and apply filters about what we should and should not sniff for. Now it is time to actually capture some packets.There are two main techniques for capturing packets. We can either capture a single packet at a time, or we can enter a loop that waits for n number of packets to BE WILL BEGIN BY LOOKING AtHow To Capture A Single Packet, The Look at Methods of Using Loops. for this We Use PCAP_NEXT (). The protoype for pcap_next () Fairly Simple:

u_char * pcap_next (pcap_t * p, struct pcap_pkthdr * h) The first argument is our session handler. The second argument is a pointer to a structure that holds general information about the packet, specifically the time in which it was sniffed, the length of this packet, and the length of his specific portion (incase it is fragmented, for example.) pcap_next () returns a u_char pointer to the packet that is described by this structure. We'll discuss the technique for actually reading the packet itself later .Here is a simple demonstration of using pcap_next () to sniff a packet. #Include #include int main () {pcap_t * handle; / * session handle * / char * dev; / * The device to sniff on * / char errbuf [PCAP_ERRBUF_SIZE]; / * Error string * / struct bpf_program filter; / * The compiled filter * / char filter_app [] = "port 23"; / * The filter expression * / bpf_u_i NT32 MASK; / * OUR NETMASK * / BPF_U_INT32 NET; / * OUR IP * / STRUCT PCAP_PKTHDR Header; / * The Header That PCAP GIVES US * / Const U_CHAR * PACKET; / * THE ACTUAL PACKET * / / * DEFINE THE DEVICE * / dev = pcap_lookupdev (errbuf); / * Find the properties for the device * / pcap_lookupnet (dev, & net, & mask, errbuf); / * Open the session in promiscuous mode * / handle = pcap_open_live (dev, BUFSIZ, 1, 0 , errbuf); / * Compile and Apply the filter * / pcap_compile (Handle, & Filter, Filter_APP, 0, NET); PCAP_SETFILTER (Handle, & Filter);

/ * Grab a packet * / packet = pcap_next (handle, & header); / * print its length * / printf ("Jacked a packet with length of [% d] / n", header.len; / * And Close THE session * / pcap_close (handle); return (0);.} This application sniffs on whatever device is returned by pcap_lookupdev () by putting it into promiscuous mode It finds the first packet to come across port 23 (telnet) and tells the user the size of the packet (in bytes). Again, this program includes a new call, pcap_close (), which we will discuss later (although it really is quite self explanatory) .The other technique we can use is more complicated, and probably more useful. Few sniffers (if any) actually use pcap_next (). More often than not, they use pcap_loop () or pcap_dispatch () (which then themselves use pcap_loop ()). To understand the use of these two functions, you must Understand the idea of ​​a callback function.callback functions area not any Anything New, And Are Very Common In Many API '

s. The concept behind a callback function is fairly simple. Suppose I have a program that is waiting for an event of some sort. For the purpose of this example, lets pretend that my program wants a user to press a key on the keyboard. Every time they press a key, I want to call a function which then will determine that to do. The function I am utilizing is a callback function. Every time the user presses a key, my program will call the callback function. Callbacks are used in pcap, but instead of being called when a user presses a key, they are called when pcap sniffs a packet. The two functions that one can use to define their callback is pcap_loop () and pcap_dispatch (). pcap_loop () and pcap_dispatch ( ) are very similar in their usage of callbacks. Both of them call a callback function every time a packet is sniffed that meets our filter requirements (if any filter exists, of course. If not, then all packets that are sniffed are sent to the Callback.) The Prototype for PCAP_L oop () is below: int pcap_loop (pcap_t * p, int cnt, pcap_handler callback, u_char * user) The first argument is our session handle Following that is an integer that tells pcap_loop () how many packets it should sniff for before returning. (a negative value means it should sniff until an error occurs). The third argument is the name of the callback function (just it's identifier, no parenthesizes). The last argument is useful in some applications, but many times is simply set as NULL . Suppose we have arguments of our own that we wish to send to our callback function, in addition to the arguments that pcap_loop () sends. This is where we do it. Obviously, you must typecast to a u_char pointer to ensure the results make It there.

as we will see later, pcap makes use of some very interesting means of passing information in the form of a u_char pointer. After we show an example of how pcap does it, it should be obvious how to do it here. If not, consult your local C reference text, as an explanation of pointers is beyond the scope of this document. pcap_dispatch () is almost identical in usage. The only difference between pcap_dispatch () and pcap_loop () is in how they handle timeouts (remember how you could set a timeout when you called pcap_open_live ()? This is where it comes into play. pcap_loop () ignores the timeout while pcap_dispatch () does not. For a more in depth discussion of their differences, see the pcap man page.Before we can provide an example of using pcap_loop (), we must examine the format of our callback function We can not arbitrarily define our callback's prototype;. otherwise, pcap_loop () would not know how to use the function So we use this format as the prototype for. Our Callback func tion:.. void got_packet (u_char * args, const struct pcap_pkthdr * header, const u_char * packet); Lets examine this in more detail First, you'll notice that the function has a void return type This is logical, because pcap_loop ( ) would not know how to handle a return value anyway. The first argument corresponds to the last argument of pcap_loop (). Whatever value is passed as the last argument to pcap_loop () is passed to the first argument of our callback function every time the function is called The second argument is the pcap header, which contains information about when the packet was sniffed, how large it is, etc. The pcap_pkthdr structure is defined in pcap.h as:. struct pcap_pkthdr {struct timeval ts;

/ * Time stamp * / bpf_u_int32 caplen; / * length of portion present * / bpf_u_int32 len; / * length this packet (off wire) * /}; These values ​​should be fairly self explanatory The last argument is the most interesting of them. all, and the most confusing to the average novice pcap programmer. It is another pointer to a u_char, and it contains the entire packet, as sniffed by pcap_loop (). But how do you make use of this variable (named "packet" in our prototype)? A packet contains many attributes, so as you can imagine, it is not really a string, but actually a collection of structures (for instance, a TCP / IP packet would have an Ethernet header, an IP header, a TCP header, and lastly, the packet's payload). This u_char is the serialized version of these structures. to make any use of it, we must do some interesting typecasting.First, we must have the actual structures define before we can typecast to them. The Following Is The Structure Definitions That I Use to Describe A TCP / IP packet over Ethernet. All three definitions that I use are taken directly out of the POSIX libraries. Normally I would have simply just used the definitions in those libraries, but it has been my experience that the libraries vary slightly from platform to platform, making it . complicated to implement them quickly So for demonstration purposes we will just avoid that mess and simply copy the relevant structures All of these, incidentally, can be found in include / netinet on your local Unix system Here are the structures:.. / * Ethernet Header * / STRUCT SNIFF_ETHERNET {u_char Ether_DHOST [Ether_ADDR_LEN]; / * Destination Host Address * / U_CHAR Ether_SHOST [Ether_ADDR_LEN]; / * Source Host Address * / U_SHORT Ether_TYPE;

/ * IP? ARP? RARP? Etc * /}; / * ip header * / struct sniff_ip {#if byte_order == little_endian u_int ip_hl: 4, / * header length * / ip_v: 4; / * Version * / #IF BYTE_ORDER == BIG_ENDIAN U_INT IP_V: 4, / * VERSION * / IP_HL: 4; / * HEADER Length * / #ENDIF #ENDIF / * NOT _IP_VHL * / U_CHAR IP_TOS; / * TYPE OF Service * / u_short ip_len; / * Total length * / u_short ip_id; / * identification * / u_short ip_off; / * fragment offset field * / #define IP_RF 0x8000 / * reserved fragment flag * / #define IP_DF 0x4000 / * dont fragment flag * / #define IP_MF 0x2000 / * more fragments flag * / #define IP_OFFMASK 0x1fff / * mask for fragmenting bits * / u_char ip_ttl; / * time to live * / u_char ip_p; / * protocol * / u_short ip_sum; / * checksum * / struct in_addr ip_src, ip_dst; / * Source and dest address * /}; / * tcp header * / struct sniff_tcp {u_short th_sport; / * source port * / u_short tr_dport; / * destination port * / tcp_seq tH_seq; / * sequence number * / tcp_s EQ TH_ACK; / * ACKNOWEDGEMENT NUMBER * / #IF BYTE_ORDER == Little_Endian U_INT TH_X2: 4, / * (Unused) * / TH_OFF: 4; / * Data Offset * / #ENDIF #if Byte_Order == BIG_ENDIAN U_INT TH_OFF: 4, / * data offset * / th_x2: 4; / * (unused) * / #endif u_char th_flags; #define TH_FIN 0x01 #define TH_SYN 0x02 #define TH_RST 0x04 #define TH_PUSH 0x08 #define TH_ACK 0x10 #define TH_URG 0x20 #define TH_ECE 0x40 #define TH_CWR 0x80 #define TH_FLAGS (TH_FIN | TH_SYN | TH_RST | TH_ACK | TH_URG | TH_ECE | TH_CWR) u_short th_win; / * window * / u_short th_sum; / * checksum * / u_short th_urp; / * urgent pointer * /};

Note: On my Slackware Linux 8 box (stock kernel 2.2.19) I found that code using the above structures would not compile The problem, as it turns out, was in include / features.h, which implements a POSIX interface unless _BSD_SOURCE. is defined. If it was not defined, then I had to use a different structure definition for the TCP header. The more universal solution, that does not prevent the code from working on FreeBSD or OpenBSD (where it had previously worked fine), is simply to do the following: #define _BSD_SOURCE 1prior to including any of your header files This will ensure that a BSD style API is being used Again, if you do not wish to do this, then you can simply use the alternative TCP.. Header Structure, Which I've Linked To Here , Along with some quick not of this reled to pcap and our mysterious u_char Well, AS Luck Would Have IT, PCAP Uses The Exact Same Structures When Sniffing Packets. THEN THEY SIMPLY create a u_char string and stuff the structures into it. So how can we break it apart? Be prepared to witness one of the most practical uses of pointers (for all of those new C programmers who insist that pointers are useless, I smite you) .Again, we're going to assume that we are dealing with a TCP / IP packet over Ethernet This same technique applies to any packet;. the only difference is the structure types that you actually use So lets begin by declaring the variables we. will need to deconstruct the packet u_char.const struct sniff_ethernet * ethernet; / * The ethernet header * / const struct sniff_ip * ip; / * The IP header * / const struct sniff_tcp * tcp; / * The TCP header * / const char * Payload;

/ * Packet payload * // * For readability, we'll make variables for the sizes of each of the structures * / int size_ethernet = sizeof (struct sniff_ethernet); int size_ip = sizeof (struct sniff_ip); int size_tcp = sizeof (struct sniff_tcp); And now we do our magical typecasting: ethernet = (struct sniff_ethernet *) (packet); ip = (struct sniff_ip *) (packet size_ethernet); tcp = (struct sniff_tcp *) (packet size_ethernet size_ip); payload = (u_char *) (packet size_ethernet size_ip size_tcp);? How does this work Consider the layout of the packet u_char in memory Basically, all that has happened when pcap stuffed these structures into a u_char is that all of the. data contained within them was put in a string, and that string was sent to our callback. The convenient thing is that, regardless of the values ​​set to these structures, their sizes always remains the same. On my workstation, for instance, a sniff_ethernet Structure Has a size of 14 Bytes. A Sniff_ip Structure IS 20 BYTES, AND LIKEWI se a sniff_tcp structure is 20 bytes The u_char pointer is really just a variable containing an address in memory That's what a pointer is;.. it points to a location in memory For the sake of simplicity, we'll say that the address this. pointer is set to is the value X. Well, if our three structures are just sitting in line, the first of them (sniff_ethernet) being located in memory at the address X, then we can easily find the address of the other structures. So Lets make a chart: variable location (in Bytes) Sniff_Thernet X SNIFF_IP X 14 SNIFF_TCP X 14 20 PayLoad X 14 20

20 The sniff_ethernet structure, being the first in line, is simply at location X. sniff_ip, who follows directly after sniff_ethernet, is at the location X, plus however much space sniff_ethernet consumes (14 in this example). Sniff_tcp is after both sniff_ip and sniff_ethernet, so it is location at X plus the sizes of sniff_ethernet and sniff_ip (14 and 20 byes, respectively). lastly, the payload (which is not really a structure, just a character string) is located after all of them.Note : It is important that you not assume your variables will have these sizes you should always use the sizeof () function to ensure that your sizes are accurate This is because the members of each of these structures can have different sizes on different platforms... So at this point, we know how to set our callback function, call it, and find out the attributes about the packet that has been sniffed It's now the time you have been waiting for:. writing a useful packet sniffer Because of the length. Of The Source Code, I'm Not Going to Include It in The Body Of this Document. Simply Download Sniffer.c and try it out.wrapping upat this point you should be able to write a sniffer using pcap. You have learned the basic concepts behind opening a pcap session, learning general attributes about it, sniffing packets, applying filters, and using callbacks. Now it's time to get out there sniff those wires! Pcap Program Design Tim Carstens This article is found in Let's take a look at this article. Obviously, some C language basics is needed unless you just want to know the basic theory. You don't have to be an encoded expert, because this area has only experienced programmers involved, and I will describe these concepts as much as possible. In addition, considering this is about a bag sniffer, the understanding of the network basics is helpful. All code examples that appear here are passed on the FreeBSD 4.3 platform.

Start: The first thing we have to understand is the overall layout of PCAP-based sniffer program. The process is as follows:

1. From decision which interface is used to sniff start. In Linux, 2. This may be ETH0, 3. And 4. In the BSD system, it may be XL1, 5. and many more. We can also define this device 6 with a string. 7. Or use the interface name 8 provided by PCAP. Come work. 9. Initialize PCAP. Here we want to tell PCAP to what device 10. Sniff. If you prefer, 11. We can also sniff the plurality of devices 12. . How to distinguish between them? Use the file handle. Just like opening a file for reading and writing, 13. Must be named 14. Our sniffing "session", 15. This will distinguish between them. 16. If we just want to sniff specific transmission (such as TCP / IP packets, 17. Package 23, 18., Etc.), 19. We must create a set of rules, 20. Compile and use it. This process is divided into three tightly associated phases. The rule set is placed in a string, 21. And converted into a format read by PCAP (so compiling it). Compiling is actually calling a no 22 in our program. Functions used by external programs. Next, we must tell PCAP to use it to filter out the one we want. twenty three. Finally, 24. We tell PCAP to enter its body to perform a loop. In this stage, 25. PCAP has been working until it receives all the packages we want. Whenever it receives a package to call another function, 26. This function can do anything we want, 27. It can analyze the package obtained by the part and print out the result, 28. It can save the results as a file, 29. Or anything is not 30. Work. 31. After sniffing the required data, 32. We have to close the session and end. This is actually a very simple process. One of the five steps, one (third) is optional. Why don't we look at how to implement every step?

Set a device

This is very simple. There are two ways to set the equipment you want to sniff. The first, we can make users tell us. To examine the following procedures: #include #include int main (int Argc, char * argv []) {char * dev = argv [1]; printf ("Device:% S / N ", DEV); RETURN (0);} The user specifies the device by passing to the first parameter of the program. The string "dev" saves the name of the interface we want to sniff with the PCAP "Understanding" format (of course, the user must give us a truly existing interface). Another is also the same. Look at this program: #include #include int main () {char * dev, errbuf [pcap_errbuf_size]; dev = pcap_lookupdev (Errbuf); Printf ("Device:% s / n ", dev); return (0);} In this example, PCAP sets the device yourself. "But, wait, TIM", you will say, "What is the string errbuf?" Most PCAP commands allow us to deliver strings as parameters to them. What is the purpose of this string? If the command fails, it will pass this string about the description of the error. This way, if the PCAP_LOOKUPDEV () fails, it will store the error message in Errbuf. Very good, isn't it? This is how we set the device. Open the device to sniff

The task for creating a sniffing session is really simple. To do this, we use the PCAP_OPEN_LIVE () function. The prototype of this function (based on the PCAP page) is as follows: PCAP_T * PCAP_OPEN_LIVE (Char * Device, int SNAPLEN, INT Promisc, int to_ms, char * ebuf) The first parameter is the device we specified in the previous section, Snaplen is shaped, it defines the maximum number of bytes that will be captured by PCAP. When promisc is set to TRUE, the specified interface is a mixed mode (however, it is also possible when it is set to FALSE. TO_MS is a timeout value when reading, and the unit is milliseconds (if 0 has been sniffing until the error occurs, it is not determined to be -1). Finally, EBUF is a string we can store any error message (like Errbuf above). This function returns its session handle.

For example, examine the following code segment: #include ... pcap_t * handle; handle = pcap_open_live (Somedev, Bufsiz, 1, 0, Errbuf);

This code snippet opens the string Somedev's device, tells it to read the number of bytes specified by Bufsiz (BUFSIZ defines in PCAP.H). We tell it that the device is set to a mixed mode, and I have been sniffing the error. If there is an error, store it in the string errbuf.

The difference between mixed mode and non-mixed mode: these two methods are very different. In general, in the sniffer of the non-mixed mode, the host only sniffs those communications directly related to it, such as swinging it, or by it, or by roads, etc. will be captured by the sniffer. In the mixed mode, you sniff all communication on the transmission line. In a non-cross-switching network, this will be a communication over the entire network. This is the most obvious advantage that more packages are sniffed, they are helpful because of your sniffing network or have no. However, the mixed mode can be detected. A host can determine if another host is performing a sniffing mode in the mixed mode by high-intensity test. Second, it works only in a non-cross-switching network environment (such as hubs, or ARP levels in exchange). Again, in a high-load network, the host's system resources will consume very serious. Filter communication is usually, our sniffer is only interested in a particular communication. For example, sometimes we want to sniff the port 23 (telnet) package to get a password; or we want to intercept the file transmitted through port 21 (ftp); may we only want to get DNS communication (port 53, udp) . In either case, we have little blindly sniffing the communication of the entire network. PCAP_Compile () and PCAP_SETFILTER () will be discussed below. This process is very simple. When we have called PCAP_OPEN_LIVE () to establish a sniffing session, we can apply our own filters. Why use our own filter? There are two reasons. First, the filter of PCAP is too powerful because it uses BPF filters directly, we skip a lot of joints by using BPF driver. Second, it is easy to do.

You must compile it before using our own filters. Filter expressions are saved in a string (array). It is proven to be very good in TCPDUMP's man page. I suggest you read it personally. But we will use simple test expressions so you may easily understand my example.

We call PCAP_Compile () to compile it, its prototype is defined: int PCAP_Compile (PCAP_T * P, Struct BPF_Program * FP, CHAR * STR, INT OPTIMIZE, BPF_U_INT32 NETMASK) The first parameter is the session handle (PCAP_T * HANDLE In the example of the previous section). Next is a reference to the address of the compiled filter version. Then the next is the expression itself, stored in the specified string format. Right down is a definition of whether the expression is optimized (0 is False, 1 is True, the standard specified). Finally, we must specify a network mask to apply this filter. The function returns -1 to fail, any other value indicates that it is successful.

The expression can be used after being compiled. Now enter PCAP_SETFILTER (). Introducing us in the format of PCAP, first take a look at the prototype of PCAP_SETFILTER ():

INT PCAP_SETFILTER (PCAP_T * P, Struct BPF_Program * FP)

This is very intuitive, the first parameter is the session handle, the second parameter is a reference to the compiled expression version (which can speculate on the second parameter of the PCAP_Compile ()). The following code examples may make you better understand: #include ... pcap_t * handle; / * Handle of the session * / char dev [] = "rl0"; / * Perform a sniffing device * / CHAR ERRBUF [PCAP_ERRBUF_SIZE]; / * Store error message string * / struct bpf_program filter; / * has compiled well filter express * / char filter_app [] = "port 23"; / * Filter expression * / BPF_U_INT32 MASK; / * Execute the network mask of the sniffing device * / bpf_u_int32 net; / * Perform a sniffing device IP address * / PCAP_LOOKUPNET (dev, & net, & mask, errbuf); handle = pcap_open_live (dev, bufsiz, 1, 0, errbuf; PCAP_Compile (Handle, & Filter, Filter_APP, 0, NET); PCAP_SETFILTER (Handle, & Filter); This program sniffer detects all communication via port 23, using a mixed mode, and the device is RL0.

You may notice that the previous example contains a function we have not mentioned: PCAP_LOOKUPNET (), providing the device interface name to this function, it will return its IP and network mask, which is very basic because we need to know the network Mask to apply a filter. This function is also described in the last Miscellaneous section in this article.

According to my experience, this filter will not work under all operating systems. In my test environment, I found OpenBSD 2.9 default kernel to support this filter, but FreeBSD 4.3 default kernel is not supported. Your situation may change.

Actual sniffing

To this end, we have learned how to define a device, let it get a sniff, and apply a filter to make us smell what or not sniff. Now it is time to really capture some packets. There are two ways to capture the package. We can only capture a package at a time, or you can enter a loop, wait for a plurality of packages and processes. Let's take a look at how to capture a single package, then look at the method of using a loop. To do this, we use the function PCAP_NEXT (). PCAP_NEXT () prototype and its simple: u_char * pcap_next (pcap_t * p, struct pcap_pkthdr * h) The first parameter is the session handle, the second parameter is to point to an overall information including the current packet (time to be captured. , The length of the package, the specified portion of the structure of the structure (only one piece here, only as an example). PCAP_NEXT () returns a U_CHAR pointer to the package described by this structure. We will discuss this way to actually read the package itself.

Here is an example of how to use PCAP_NEXT () to sniff a package: #include #include int main () {pcap_t * handle; / * session handle * / char * dev; / * Perform a sniffing device * / char errbuf [pcap_errbuf_size]; / * Store error message string * / struct bpf_program filter; / * Already compiled filter * / char filter_app [] = "port 23"; / * Filter expression * / bpf_u_int32 mask; / * Mask of the network * / bpf_U_INT32 NET; / * The host's IP address * / struct PCAP_PKTHDR header; / * is defined by PCAP.H * / const u_char * packet; / * actual Package * / / * define the device * / dev = pcap_lookupdev (errbuf); / * Probe Device Properties * / PCAP_LOOKUPNET (DEV, & NET, & MASK, Errbuf); / * Open Session in Mixed Mode * / Handle = PCAP_OPEN_LIVE (DEV) , Bufsiz, 1, 0, Errbuf; / * Compile and apply filter * / pcap_compile (Handle, & Filter, Filter_APP, 0, NET); PCAP_SETFILTER (Handle, & Filter); / * Intercepting a package * / packet = PCAP_NEXT Handle, & header); / * Print its length * / Printf ("Jacked a packet with length of [% d] / n", header.len); / * Close Session * / PCAP_Close (Handle); Return (0);} This program sniffs the device returned by PCAP_LOOKUPDEV () And set it to a mixed mode. It finds that the first package passes through the port 23 (telnet) and tells the user that the size of this package (in bytes). This program also includes a new call PCAP_CLOSE (), we will discuss later (although its name is enough to prove its own role). Another means we can use is more complicated and may be more useful. I have few (if any) The sniffer really uses PCAP_NEXT (). Typically, they use PCAP_LOOP () or PCAP_DISPATCH () (which is PCAP_LOOP ()). In order to understand the use of these two functions, you must understand the idea of ​​callback function.

The callback function is not new, it is very common in many APIs. The concept of the callback function is extremely simple. Imagine that I have a program that is waiting for some sort. In order to achieve this example, let's imagine my program to let the user press a key on the keyboard, whenever they pressed a key, I want to call a function that works accordingly. The function I use is a callback function. Each time the user presses a key once, my program calls the callback function once. The callback function is in the application in the PCAP, replacing the function called when the user presses keys, is the function called when the PCAP sniffs when a packet is detected. Two functions that can define their callback functions are PCAP_LOOP () and PCAP_DISPATCH (). This two are very similar in their use of their callback functions. They are all captured a package callback function when conforming to our filter (of course, if there is a filter, if there is no existence, all sniffed packages are sent to the adjustable function.). The prototype of the PCAP_LOOP () is as follows:

INT PCAP_LOOP (PCAP_T * P, INT CNT, PCAP_HANDLER CALLBACK, U_CHAR * USER)

The first parameter is a session handle, which is an integer that tells PCAP_LOOP () how many packets should be captured before returning (if a negative value indicates that should be working until the error occurs). The third parameter is the name of the callback function (positive as indicated by its identifier, hanging). The last parameter is useful in some applications, but more time is NULL. Suppose we have the parameters of our own call to the callback function, and the parameters sent by PCAP_LOOP (), which requires it. Obviously, it must be a u_char type pointer to ensure the result is correct; as we see later, PCAP uses a very interesting method to pass information on the situation of the U_CHAR pointer. It's easy to do after we show how PCAP is doing. If you can't do it, please refer to your local C reference text, as an explanation of a pointer, it exceeds the scope of this article. The usage of PCAP_DISPATCH () is almost the same. The only difference is how they handle timeout (how do you remember how to set up timeout when calling PCAP_OPEN_LIVE ()? This is where it works). PCAP_LOOP () ignores the timeout and PCAP_DISPATCH () is not. See the PCAP man page with a more in-depth discussion between them.

Before providing examples of using PCAP_LOOP (), we must check the format of our callback function. We cannot define the prototype of the callback function, otherwise PCAP_LOOP () will not know how to use it. So we use this format as prototype of our callback function: void got_packet (u_char * args, const struct pcap_pkthdr * header, const u_char * packet); let us more detail it. First, you will notice that the function returns a Void type, which is logical because PCAP_LOOP () does not know how to process a callback return value. The first parameter corresponds to the last parameter of PCAP_LOOP (). Whenever the callback function is called, this value is passed to the first parameter of the callback function regardless of the last parameter to PCAP_LOOP (). The second parameter is defined by the PCAP header file, which includes information such as time, size, and other information. The structural PCAP_PKHDR is defined in PCAP.H as follows: struct pcap_pkthdr {structure TimeVal TS; / * Timestamp * / BPF_U_INT32 CAPLEN; / * The length of the captured part * / bpf_u_int32 Len; / * Offline length * /} These quantities are quite clear. The last parameter is most interesting in them, and the newcomers of PCAP procedures are also confused. This is another U_CHAR pointer that contains all the packages that are sniffed by PCAP_LOOP (). But how do you use this variable we are called packet in prototype? A packet contains many properties, so you can imagine it is not just a string, and it is essentially a collection of structures (for example, a TCP / IP package has an Ethernet head, one IP header, one The TCP header, there is a payload of this package). This U_CHAR is the series version of these structures. In order to use it, we must do some interesting matching work.

First, these actual structures must be defined before matching them. Below is the definition of the structure of the TCP / IP package of the Ethernet. All of these definitions I use are directly extracted from the POSIX library. Usually, I only use the definitions in those libraries, but according to my experience, there is a slight difference between the libraries of different platforms, which makes it achieved. Therefore, in order to achieve the purpose, I avoid the confusing and simple copying of these associated structures. All of this can be found in the include / Netinet in your local UNIX system.

Below is these structures: / * Ethernet frame head * / struct sniff_ethernet {u_char Ether_DHOST [Ether_ADDR_LEN]; / * Destination Host address * / u_char Ether_SHOST [Ether_ADDR_LEN]; / * Source Host address * / u_Short Ether_TYPE; / * IP? ARP? RARP? Etc * /}; / * IP packet header * / struct sniff_ip {#if Byte_Order == Little_Endian U_INT IP_HL: 4, / * Head Length * / IP_V: 4; / * Version No. * / #if byte_order == BIG_ENDIAN U_INT IP_V: 4, / * version number * / ip_hl: 4; / * Head length * / #ENDIF #ENDIF / * NOT _IP_VHL * / U_CHAR IP_TOS; / * Service Type * / U_short ip_len; / * Total length * / u_short ip_id; / * Package sign number * / u_short ip_off; / * Debris offset * / #define ip_rf 0x8000 / * Reserved Debris Sign * / #define ip_df 0x4000 / * DONT FRAGMENT Flag * / #define ip_mf 0x2000 / * multi-fragment flag * / #define ip_offmask 0x1ff / * segment bit * / u_char ip_ttl; / * Data packet survival * / u_char ip_p; / * Used protocol * / u_short ip_sum ; / * Check and * / Struct in_addr ip_src, ip_dst; / * source address, destination address * /}; / * TCP packet header * / struct sniff_tcp {u_short th_sport; / * source port * / u_short tr_dport; / * Destination Port * / TCP_SEQ TH_SEQ; / * 包序 number * / TCP_SEQ TH_ACK; / * Confirmation number * / #IF byte_order == little_endian u_int TH_X2: 4, / * has not been used * / TH_OFF: 4; / * Data Offset * / #ENDIF #if Byte_Order == BIG_ENDIAN U_INT TH_OFF: 4, / * data offset * / th_x2: 4; / * not used * / #endif u_char th_flags; #define TH_FIN 0x01 #define TH_SYN 0x02 #define TH_RST 0x04 #define TH_PUSH 0x08 #define TH_ACK 0x10 #define TH_URG 0x20 #define TH_ECE 0x40 #define TH_CWR 0x80 #define th_flags (TH_FIN | TH_SYN | TH_RST | TH_ACK | TH_URG | TH_ECE | TH_CWR) u_short tr_win; / * TCP Slide window * / u_short th_sum; / * Head check and * / u_short t_urp; / * emergency Service bit * /};

Note: I found that the code to use the above structure will not be compiled by compilation. Later, the question was in incrude / fearures.h, which only implemented a POSIX interface unless defined BSD_Source. If it is not defined, I can only define the TCP header using a different structure. Make them more common solutions on the FreeBSD or OpenBSD system as follows: #define _bsd_source 1 To include all your header files yourself. This will ensure that the BSD style API is used normally. If you don't want to do this, then you can change the TCP header structure (click this link, including comments). So how all these is related to PCAP? Look, the luck is the structure that is used when the PCAP sniffing packet is used. Next, it creates a U_CHAR string and fills the structures. So how can we distinguish between them? Prepare one of the most practical benefits of witness the pointer (here, I have to stimulate the new C progents who insist on saying that the pointer is useless).

We have once again assumed it to process the TCP / IP packets on the Ethernet. The same means can be applied to any packet, the only difference is the type of structure you actually used. Let us start from the declaration of the u_CHAR package: const struct sniff_ethernet * Ethernet; / * Ethernet frame head * / const struct sniff_ip * ip; / * IP Baotou * / const struct Sniff_tcp * TCP; / * TCP Baotou * / const char * payload; / * Ticket payload * // * In order to make it readable, we calculate the variable size in each structure * / int size_ethernet = sizeof (strunt sniff_ethernet); int size_ip = sizeof (struct sniff_ip); int size_tcp = sizeof (struct sniff_tcp); now we begin to feel a bit mysterious match: ethernet = (struct sniff_ethernet *) (packet); ip = (struct sniff_ip *) (packet size_ethernet) ; tcp = (struct sniff_tcp *) (packet size_ethernet size_ip); payload = (u_char *) (packet size_thernet size_ip size_tcp);

How do you work here? Consider the level of u_CHAR in memory. Basically, when PCAP fills these structures in U_CHAR, the data is stored in a string, and the string will be sent to our adjustment function. Reverse conversion is such that the value in these structural systems is not considered, and their size will be consistent. For example, on my platform, a SNIFF_ETHERNET structure is 14 bytes. A sniff_ip structure is 20 bytes, and a SNIFF_TCP structure is also 20 bytes. The U_CHAR pointer is a variable containing the memory address, which is also the essence of the pointer, which points to one area of ​​memory. Simply put, we said that the address of the pointer points is x, if the three structures are aligned, the first (sniff_ethernet) is loaded into the X-based address of the memory address, we can easily discover the address of other structures, let us Displayed in the table:

Variable Location (in Bytes) Sniff_Thernet X SNIFF_IP X 14 SNIFF_TCP X 14 20 PayLoad X 14 20 Structural Sniff_Thernet is just in X, followed by its sniff_ip, located in X plus its own occupied space ( This example is 14 bytes), and all addresses are pushed.

Note: You don't assume that your variable is also very important. You should always use sizeof () to make sure the size is correct. This is because each member in these structures can have different sizes under different platforms.

To now, we already know how to set up a callback function, call it, and clarify the properties of the sniffed packet. You may be looking forward to writing an available bag sniffer. Because of the length of code, I don't want to be listed in this article. You can download and test it here.

Conclusion, you should be able to write a PCAP-based package sniffer. You have already learned the basic concept: Open a PCAP session, about its full property, sniffing the packet, using a filter, use a callback function, and so on. It is time to sniffing the packet.

转载请注明原文地址:https://www.9cbs.com/read-26158.html

New Post(0)