I'm trying to improve my C/network knowledge implementing a ARP spoofing tool with Pcap library.
I'm stuck with sniffing arp packets. I can detect the ARP type in the ethertype field of Ethernet frame. But when I "read" the ARP packet, all values are 0 (null) but hardware addr(MAC) & protocol address(ip) are very weird 8 byte number repeated (like 20e54ef12:20e54ef12:20e54ef12...). I just can't figure it out. Here is what I've done so far :
packet_struct.h (the different structures used for eth, arp, ip...)
#ifndef DEF_PACKET_STRUCT
#define DEF_PACKET_STRUCT
#include <sys/types.h>
#define BUFF_SIZE 1518
#define ETH_SIZE 14
#define ARP_SIZE 28
/* in bytes */
#define ETH_ADDR_SIZE 6
#define IP_ADDR_SIZE 4
typedef struct pkt_eth {
unsigned char dest[ETH_ADDR_SIZE];
unsigned char src[ETH_ADDR_SIZE];
unsigned short type;
} pkt_eth;
#define ETHERTYPE_ARP 0x0806
#define ARP_REQUEST 1
#define ARP_REPLY 2
typedef struct pkt_arp {
unsigned short htype;/* hardware type => ethernet , etc */
unsigned short ptype; /*protocol type => ipv4 or ipv6 */
unsigned char hard_addr_len; /* usually 6 bytes for ethernet */
unsigned char proto_addr_len; /*usually 8 bytes for ipv4 */
unsigned short opcode; /* type of arp */
unsigned char hard_addr_send[ETH_ADDR_SIZE];
unsigned char proto_addr_send[IP_ADDR_SIZE];
unsigned char hard_addr_dest[ETH_ADDR_SIZE];
unsigned char proto_addr_dest[IP_ADDR_SIZE];
} pkt_arp;
#define ETHERTYPE_IP 0x0800
typedef struct pkt_ip {
unsigned char vhl;
unsigned char tos;
unsigned short len;
unsigned short id;
unsigned short off;
unsigned char ttl;
unsigned char proto;
unsigned short crc;
unsigned int addr_src;
unsigned int addr_dest;
} pkt_ip;
#endif
packet_print.c (utilities to print packet information )
#include "packet_struct.h"
#include <stdio.h>
#include <stdlib.h>
char * to_addr(unsigned char * addr, int length) {
int i = 0;
char string[length];
for(i=0; i< length; i++)
sprintf(string,"%02x:",addr[i]);
return string;
}
void print_pkt_eth(pkt_eth * eth) {
int i = 0;
fprintf(stdout,"Ethernet Layer \n");
fprintf(stdout,"\tSource:\t");
for(i=0;i<ETH_ADDR_SIZE;i++)
fprintf(stdout,"%02x:",eth->src[i]);
//fprintf(stdout,"%s",to_addr(eth->src,ETH_ADDR_SIZE));
fprintf(stdout,"\n\tDest:\t");
for(i=0;i<ETH_ADDR_SIZE;i++)
fprintf(stdout,"%02X:",eth->dest[i]);
if(ntohs(eth->type) == ETHERTYPE_IP)
fprintf(stdout,"\n\tType:\t IPv4");
else if(ntohs(eth->type) == ETHERTYPE_ARP)
fprintf(stdout,"\n\tType:\t ARP");
printf("\n");
}
void print_pkt_arp(pkt_arp * arp) {
int op = 0;
int i = 0;
printf("ARP Layer \n");
printf("\tHardware type:\t%02d\n",arp->htype);
printf("\tProtocol type:\t%02d\n",arp->ptype);
printf("\tHardware addresses length:\t%01d\n",arp->hard_addr_len);
printf("\tProtocol addresses length:\t%01d\n",arp->proto_addr_len);
op = ntohs(arp->opcode);
printf("\tOperation code:\t%01u\n",op);
printf("\tHardware sender:\t");
for(i=0;i<ETH_ADDR_SIZE;i++)
printf("%02x:",arp->hard_addr_send);
printf("\n\tSoftware sender:\t");
for(i=0;i<IP_ADDR_SIZE;i++)
printf("%02x:",arp->proto_addr_send);
printf("\n");
}
void print_pkt_ip(pkt_ip * ip) {
}
sniffer.c ( the tool itself )
#include<stdio.h>
#include<stdlib.h>
#include<netinet/in.h> // for addresses translation
#include<errno.h>
// for ntohs etc
// can also be necessary to include netinet/in
#include <arpa/inet.h>
#include "packet_struct.h"
#include <pcap.h>
#define SNAP_LEN 1518
int packet_count = 0;
void handleARP(const struct pkt_eth * eth) {
const struct pkt_arp * arp = (const struct pkt_arp *) (eth + ETH_SIZE);
print_pkt_arp(arp);
if(ntohs(arp->htype) != 1) {
fprintf(stderr, "Error : ARP packet does not contain a Hardware type Ethernet -> %d\n",ntohs(arp->htype));
return;
}
// check protocol type
if(ntohs(arp->ptype) != 0x800) {
fprintf(stderr,"Error : ARP packet does not contain a IPv4 type\n");
return;
}
}
void sniff_callback(u_char * user, const struct pcap_pkthdr * h,const u_char * bytes) {
int i = 0;
for(i=0; i < 25; i++) { printf("-"); }; printf("\n");
printf("Received packet number %d ==> %d\n",packet_count++,h->len);
const struct pkt_eth * eth;
unsigned short eth_type;
unsigned int captureLength = h->caplen;
unsigned int packetLength = h->len;
if(captureLength != packetLength) {
fprintf(stderr,"Error : received packet with %d available instead of %d \n",captureLength,packetLength);
return;
}
if(captureLength < ETH_SIZE) {
fprintf(stderr,"Error : received too small packet , %d bytes",captureLength);
return;
}
eth = (struct pkt_eth*)(bytes);
// print the packet
print_pkt_eth(eth);
eth_type = ntohs(eth->type);
if(eth_type == ETHERTYPE_ARP) {
handleARP(eth);
}
for(i=0; i < 25; i++) { printf("-"); }; printf("\n");
return;
}
/* returns 0 if everything went well */
int set_options(pcap_t * handle) {
int ret = 0;
ret = pcap_set_promisc(handle,1);
if(ret != 0) {
fprintf(stderr,"Error setting promiscuous mode\n");
return ret;
}
ret = pcap_set_snaplen(handle,SNAP_LEN);
if(ret != 0) {
fprintf(stderr,"Error setting snapshot length\n");
return ret;
}
ret = pcap_set_timeout(handle,1000);
if(ret != 0) {
fprintf(stderr,"Error setting timeout\n");
return ret;
}
return ret;
}
int activate(pcap_t * handle) {
int ret = pcap_activate(handle);
switch(ret) {
case 0:
fprintf(stdout,"Activation complete\n");
break;
case PCAP_WARNING_PROMISC_NOTSUP:
fprintf(stderr,"Promiscuous mode not supported\n");
return ret;
case PCAP_ERROR_PERM_DENIED:
fprintf(stderr,"Not have the permission required\n");
return ret;
case PCAP_ERROR_PROMISC_PERM_DENIED:
fprintf(stderr,"Not have the permission required for promiscuous\n");
return ret;
default:
fprintf(stderr,"Error occured during activation, see code\n");
return ret;
}
return ret;
}
/* Will activate device , filter & call the sniffing loop */
int sniffing_method(char * interface, char * filter,int packet_count) {
char err[PCAP_ERRBUF_SIZE]; //error buffer
pcap_t * handle; // handler of the interface by pcap
struct bpf_program bpf;
bpf_u_int32 mask; // network mask
bpf_u_int32 ip; // network ip
struct in_addr addr; // network number
int ret;
/* get mask & ip */
if(pcap_lookupnet(interface, &ip, &mask, err) == -1) {
fprintf(stderr, "Couldn't get netmask for device %s: %s\n",interface,err);
exit(EXIT_FAILURE);
}
handle = pcap_create(interface,err);
if (handle == NULL) {
fprintf(stderr,"Error pcap_create() : %s \n",err);
exit(EXIT_FAILURE);
}
if(set_options(handle) != 0) {
fprintf(stderr,"Exiting\n");
exit(EXIT_FAILURE);
}
if (activate(handle) != 0) {
fprintf(stderr,"Exiting\n");
exit(EXIT_FAILURE);
}
/* FILTER PART */
if(filter != NULL) {
if(pcap_compile(handle,&bpf,filter,0,ip) == -1){
fprintf(stderr,"Couldn't compile filter expr %s : %s\n",filter,pcap_geterr(handle));
exit(EXIT_FAILURE);
}
if(pcap_setfilter(handle, &bpf) == -1) {
fprintf(stderr,"Couldn't install filter %s : %s\n",filter,pcap_geterr(handle));
exit(EXIT_FAILURE);
}
}
/* SNIFF starts */
printf("Sniffing starting on %s ...\n",interface);
pcap_loop(handle,packet_count,sniff_callback,NULL);
pcap_freecode(&bpf);
pcap_close(handle);
return EXIT_SUCCESS;
}
void usage() {
printf("sniff interface [filter] [count]");
printf("interface is the interface you want to listen on. It will try to put it in monitor mode");
printf("filter can be a filter for libpcap to apply for packets it reads");
}
int main(int argc, char * argv[])
{
int i = 0; // counter
int ret;
char * default_filter = "ip";
char * filter;
int pcount = -1; //take all packet by defaults
char * interface;
if(argc < 2) {
fprintf(stderr, "No interfaces specified in arguments\n");
usage();
exit(EXIT_FAILURE);
}
// take command line filter
if(argc > 2) {
filter = argv[2];
} else {
filter = default_filter;
}
// take command line packet count limit
if(argc > 3) {
pcount = atoi(argv[3]);
}
fprintf(stdout,"Args : ");
for(i = 0; i < argc; i++) {
fprintf(stdout,"\t%s",argv[i]);
}
printf("\n");
interface = argv[1];
sniffing_method(interface,filter,pcount);
}
And here is one output (all tries giving the same output anyway)
Received packet number 2 ==> 42
Ethernet Layer
Source: 00:ee:bd:aa:f4:98:
Dest: FF:FF:FF:FF:FF:FF:
Type: ARP
ARP Layer
Hardware type: 00
Protocol type: 00
Hardware addresses length: 0
Protocol addresses length: 0
Operation code: 0
Hardware sender: 20e9a152:20e9a152:20e9a152:20e9a152:20e9a152:20e9a152:
Software sender: 20e9a158:20e9a158:20e9a158:20e9a158:
Error : ARP packet does not contain a Hardware type Ethernet -> 0
-------------------------
-------------------------
Received packet number 3 ==> 42
Ethernet Layer
Source: 00:ee:bd:aa:f4:98:
Dest: FF:FF:FF:FF:FF:FF:
Type: ARP
ARP Layer
Hardware type: 00
Protocol type: 00
Hardware addresses length: 0
Protocol addresses length: 0
Operation code: 0
Hardware sender: 20e5a152:20e5a152:20e5a152:20e5a152:20e5a152:20e5a152:
Software sender: 20e5a158:20e5a158:20e5a158:20e5a158:
This part is wrong:
Here you're passing in a
struct pkt_eth*
, to which you addETH_SIZE
. Pointer arithmetic advances to the next element, not to the next byte. You're essentially lookingsizeof(struct pkt_eth) * ETH_SIZE
bytes past the pointer passed in.You should just do
(Or pass in an unsigned char * that already starts at the layer you want to decode.)