In this section we'll have a look how the IEEE 802.11 protocol is implemented in libtins.
libtins has great support for the IEEE 802.11 protocol. Tools such as those included in the aircrack-ng suite should be very simple to implement using this library.
The whole protocol is implemented using a base class,
which contains fields shared by every frame in it. Every frame type is
represented by a certain class that inherits from it.
Let's have a look at management frames, which are represented by the
Dot11Management. This class contains several helper
methods which allow you to search and add tagged options from the frame.
We'll take as an example
Dot11Beacon, which is the class that
represents beacon frames:
Note that the above curly brace syntax used when calling
Dot11Beacon::supported_rates will only work in C++11, the latest
C++ ISO standard. That method takes a
as its argument, so we're calling vector's constructor from an
initializer_list. If we didn't use this feature, we'd have to
create a temporary vector, fill it in, and pass it as argument.
If you haven't checked out C++11, you should, it's awesome.
Data frames should be easy to deal with if you have a basic notion of how higher protocols are encapsulated within 802.11.
Let's asume we've got no encryption in our data frames. A TCP packet will look like this in libtins:
SNAP class, represents the
You should only care about this structure if you're actually crafting
packets. While sniffing, you should always use the
method template to find the layer you're looking for. Note that instead
SNAP + IP + TCP, there might be just a
RawPDU if the
data is encrypted. In order to create a packet like the one shown above,
you could do the following:
libtins supports the decryption of WEP and WPA2 (both AES and TKIP) encrypted 802.11 frames. Of course, the library will not crack the password/PSK used to encrypt the packets, you need to provide it yourself.
WEPDecrypter class, which lies the namespace
is the one that handles WEP decryption. You create an object of
that type, add tuples
(bssid, password), and let it decrypt packets.
Let's see an example:
Okay, the example is pretty straightforward. One thing I'd like to point
out is why would decryption fail? Imagining the
with the data frame was actually the one for which we provided a password,
it could have happened that after decryption, the checksum field in the
data frame was invalid. In this case, the
RawPDU that contained
the data, would be removed, so
some_data.inner_pdu() would be
nullptr. I'll explain why does the class behave
this way below.
In case decryption was successful, the
RawPDU would be replaced
SNAP PDU, followed by whatever that packet contained.
As an example, this picture illustrates what would happen if you
successfully decrypted a TCP packet:
Note that everything that follows the
SNAP PDU obviously depends
on the actual packet being decrypted. Moreover, if the data frame were
not encrypted, or the associated BSSID was not the one given, the packet
will be left intact.
If you read above and wondered why packets for which the checksum is
invalid are modified, and the
RawPDU which contains the encrypted
data is removed, then here's the reason. The situation given in the
snippet shown a few lines up is not very common. If you want to decrypt
a packet, you are most surely taking it out from either your network
interface, or a pcap file. The
WEPDecrypter class was designed to
be plugged in between a sniffer and the sniffer callback. That can
be achieved using the
DecrypterProxy class template. This is
how you'd do it:
The code shown above would be a more common situation in which you'd use
WEPDecrypter. Let's analyze it:
DecrypterProxy<bool(*)(PDU&), WEPDecrypter>. Yes, it's a long name, that's why I used that specifier. This proxy class implements an
operator(), suitable to be used as the argument to
Sniffer::sniff_loop. It also stores a decrypter, in this case a
WEPDecrypter, which you can access using the method
PDUargument is a data frame that should be decrypted, then it will be. Frames from BSSIDs other than the ones provided will be left intact, and you'll still be able to process them in the same callback. Packets for which the checksum is invalid will be discarded. This somehow explains the behaviour pointed out a few lines up.
Using this design, when new decrypters are added, you'll simply chain them all together and use a somewhat "decrypting chain".
As from libtins v1.1, the library supports the decryption of
WPA2(both AES and TKIP) encrypted frames. This is
done through the
WPA2 decryption is more complex than WEP decryption, since you can't just pick a random packet and decrypt it. There's a handshake between each client and the access point in which some nonces, which are later needed during decryption, are exchanged.
Moreover, the access point's SSID is required in order to create the first set of keys(the PMK), so you have to provide that as well. The object will be analyzing beacon frames looking for that SSID so that handshakes performed against that access point can be filtered using the appropriate BSSID.
WPA2Decrypter will be looking for 4-way handshakes. Everytime
one is detected, the PTK keys are generated(the MIC in
the handshake is verified), and from that point, every packet sent from/to
that client will be decrypted, just like WEPDecrypter does.
Let's see a small example on how to do this:
As you may know, network interface drivers typically use some form of
encapsulation rather than using raw 802.11 frames. A widely used form
of encapsulation is the
RadioTap protocol. If your driver uses
RadioTap, then a packet taken from it will look like this:
The RadioTap protocol gives you more information about 802.11 frames. You can retrieve info such as the signal strength, noise, whether a Frame Check Sequence is used to provide integrity, etc.
RadioTap class uses some useful defaults, so you don't have
to provide every option, since most of the time you'll use the same value
for them. As an example, this snippet:
Generates the following packet:
Note that some drivers actually use raw 802.11 frames, without any encapsulation. Others may use AVS or Prism, but unfortunately, libtins does not support them yet. However, adding them is in the TODO list, so they'll probably be included soon.