23 Mart 2017 Perşembe

Libpcap Giriş - Ağ paketlerinin yakalanması (network traffic capturing)


Libpcap güçlü bir ağ paket analiz kütüphanesidir, ağdaki trafiği dinlemeye ve ağa paket göndermeye yarar. http://www.tcpdump.org/

Winpcap ise libpcap 'in windows versiyonudur,  https://www.winpcap.org/

libpcap gelen paketlerin henüz işletim sistemi tarafından işlenmeden evvel bir kopyasını alır. Böylece gelen paketler işletim sistemi tarafından herhangi bir işleme tabi tutulmadan alınmış olur. Libpcap C/C++ ile birlikte kullanılacak şekilde dizayn edilmiştir fakat Python, Java, C# ile kullanılabilmesi için de ara birimler (wrapper) geliştirilmiştir.


Bu yazı kapsamında vereceğim örnekte ağ kartından ziyade kayıtlı bir .pcap dosyasından alınan paketler incelenmiştir. Verilen örnekte, alınan paketler analiz edilerek kaynak ve hedef MAC adresleripaketin tipi, ve eğer paket IP paketi ise ip başlığındaki bazı bilgiler çekilmiştir.

Libpcap kütüphanesini projemizde kullanabilmemiz için projemize ekleyeceğimiz header
#include <pcap.h>

İlgili <pcap.h> dosyasını projemizde kullanabilmemiz için ilk olarak proje ayarlarında winpcap kütüphanesi için dosya yolu ayarlarını yapmamız lazım. Bunun için Visual Studio 2015'te indirmiş olduğumuz winpcap kütüphanesi için aşağıdaki ayarları yapmamız gerekmekte.

Project → Properties → C/C++ → Additional Includes'a …..\\WpdPack\include dosya yolunu

Project → Properties → Linker→ Additional Library'e …..\\WpdPack\lib\ dosya yolunu

Project → Properties → Linker→ Additional Dependencies'e ise wpcap.lib , yazmalıyız



Proje ayarlarımızı yaptıktan sonra direkt olarak koda giriş yapalım :)

int main()
{
std::string file = "C:\\users\\ibrahim\\Downloads\\smallFlows.pcap";

char errbuff[PCAP_ERRBUF_SIZE];

// libpcap'i offline modda yani dosyadan çalışacak şekilde başlatıyoruz
pcap_t * pcap = pcap_open_offline(file.c_str(), errbuff);

// alınacak paketin başlığı için header tanımlaması
struct pcap_pkthdr *header;

const u_char *data;

u_int packetCount = 0;
while (int returnValue = pcap_next_ex(pcap, &header, &data) >= 0)
{

std::cout << " Packet : " << ++packetCount << std::endl;

std::cout << " Packet size : " << header->len << std::endl;

// Show Epoch Time
std::cout << " Epoch Time: " << header->ts.tv_sec << "." << header->ts.tv_usec << " sn" << std::endl;
}

std::cin.get();
return 0;
}


Bu kodu çalıştırdığımızda alacağımız ekran çıktısı:



Örnekteki while döngüsünü inceleyecek olursak pcap_next_ex(pcap, &header, &data) fonksiyonunun paketleri sırayla okuduğunu göreceğiz. Bu örnekte yaptığımız iş sadece paket uzunluğunu header->len ve saniyesini header->ts.tv_sec ekrana yazdırmak oldu. Yani sadece header değişkenimizdeki iki değeri ekrana yazdırdık. pcap_next_ex fonksiyonun geri döndürğünü değerleri inceleyecek olursak;



struct pcap_pkthdr *header  ---> frame başlığı ile ilgili verileri içeriyor

const u_char *data  ---> ise frame içeriği ile ilgili verileri içermekte. Yani içerisinde

kapsüllenmiş şekilde Ethernet başlığı, IP başlığı,devamında UDP yada hangi protokol içeriyorsa tüm veriler  char* tipinde geri döndürülüyor.

Şimdi ilk olarak ethernet başlığından MAC bilgilerini çekelim.

std::cout << " Destination Mac Address : " ;
for (u_int i = 0; i <6; i++)
{
std::cout << std::hex << ((int)data[0]<10 ?"0":"") << (int)data[0]<< (i!=5?":" :"") ;
data++;
}
std::cout<< std::endl;

Bildiğiniz gibi MAC adresleri 6 byte yer kaplamaktadır. Ethernet başlığının ilk 6 byte'ı hedef adresi sonraki 6 byte'ı ise kaynak adresi ifade eder.Aynı işlemi kaynak MAC adresini bulmak için de tekrarlıyoruz. Son 2 byte ise Ethernet tipini belirtir. Eğer bu değer 0x0800 ise gelen frame bir IP paketidir, anlamına gelmektedir..

unsigned short ethernet_type = data[0] << 8 | data[1];
data += 2; // **** increment location on pointer, because ethernet type is 2 byte ****


Ethernet başlığındaki bilgileri çektik. Şimdi ise şayet gelen frame IP paketi içeriyorsa, IP başlığı üzerinde analiz yapıp ip paket total length, identification number ile kaynak ve hedef ip adreslerini çekeceğiz. IP paket başlığında hangi byte larda hangi bilgilerin saklandığını anlayabilmek için aşağıdaki ip paket başlığı yapısını inceleyiniz.

IP paket başlık yapısı


while (int returnValue = pcap_next_ex(pcap, &header, &data) >= 0)
{

std::cout<<" Packet : " << ++packetCount <<std::endl;

std::cout << " Packet size : "<< header->len << std::endl;

// Show Epoch Time
std::cout << " Epoch Time: " << header->ts.tv_sec << "."<< header->ts.tv_usec << " sn"<< std::endl;

std::cout << " Destination Mac Address : " ;
for (u_int i = 0; i <6; i++)
{
std::cout << std::hex << ((int)data[0]<10 ?"0":"") << (int)data[0]<< (i!=5?":" :"") ;
data++;
}
std::cout<< std::endl;


std::cout << " Source Mac Address : ";
for (u_int i = 0; i <6; i++)
{
std::cout << std::hex << ((int)data[0]<10 ? "0" : "") << (int)data[0] << (i != 5 ? ":" : "");
data++;
}
std::cout<< std::dec << std::endl;

unsigned short ethernet_type = data[0] << 8 | data[1];
data += 2; // **** ethernet type is 2 byte ****

std::cout << " Ethernet Type : " << (ethernet_type==0x0800 ? "*** IP ***" : "-----" ) << std::endl;

if (ethernet_type == 0x0800)
{
unsigned char lms = data[2];
unsigned char rms = data[3];

uint16_t totallength = (lms << 8) | rms;

lms = data[4];
rms = data[5];
unsigned short identification = (lms << 8) | rms;

unsigned int ip_source = (data[15] << 24) | (data[14] << 16) | (data[13] << 8) | data[12];

unsigned int ip_destination = (data[19] << 24) | (data[18] << 16) | (data[17] << 8) | data[16];

std::cout << " IP Packet Total length : " << totallength<< std::endl;
std::cout << " IP identification number : " << identification << std::endl;
std::cout << " IP Source: " << GetIPString(ip_source) << std::endl;
std::cout << " IP Destination: " << GetIPString(ip_destination) << std::endl;

}

std::cout << std::endl << std::endl;


if (packetCount == 4) break;
}





Not: integer tipindeki ip adreslerini x.x.x.x formatına çevirmek için kullandığımız fonksiyon.

#include <winsock2.h>
#pragma comment(lib, "Ws2_32.lib")
std::string GetIPString(uint32_t x)
{
char buffer[INET_ADDRSTRLEN + 1];
auto result = inet_ntop(AF_INET, &x, buffer, sizeof(buffer));
if (result == nullptr) throw std::runtime_error("Can't convert IP4 address");
return buffer;

}

Hiç yorum yok:

Yorum Gönder