C语言实现Linux网络嗅探器
C语言实现Linux网络嗅探器
0x01 实验简介
网络嗅探器是拦截通过网络接口流入和流出的数据的程序。所以,如果你正在浏览的互联网,嗅探器以数据包的形式抓到它并且显示。在本实验中,我们用 C 语言实现了一个网络嗅探器。
0x02程序框架和功能描述
本程序使用c语言编程,实现linux环境下网络嗅探的功能,并实现对接收到的UDP数据报进行解析。
0x03程序代码
sniffer.h
#ifndef __SNIFFER_H__
#define __SNIFFER_H__
typedef struct s_protocol
{
int tcp;
int udp;
int icmp;
int igmp;
int others;
int total;
} t_protocol;
typedef struct s_sniffer
{
FILE *logfile;
t_protocol *prot;
} t_sniffer;
void ProcessPacket(unsigned char*, int, t_sniffer *);
void print_ip_header(unsigned char* , int, t_sniffer *);
void print_tcp_packet(unsigned char* , int, t_sniffer *);
void print_udp_packet(unsigned char * , int, t_sniffer *);
void print_icmp_packet(unsigned char* , int, t_sniffer *);
void PrintData (unsigned char* , int, t_sniffer *);
void display_time_and_date();
void getting_started();
void signal_white_now(int);
#endif
tools.h
#ifndef __COLOR_H__
#define __COLOR_H__
#include <stdio.h>
#define CLEARSCREEN() printf("\033[H\033[2J")
#define INITCOLOR(color) printf("\033[%sm", color)
#define RED_COLOR "31"
#define GREEN_COLOR "32"
#define YELLOW_COLOR "33"
#define BLUE_COLOR "34"
#define ZERO_COLOR "0"
#endif
tools.c
#include <signal.h>
#include <stdio.h>
/* 信号处理函数 */
void signal_white_now(int signum)
{
printf("Bye Bye !\n");
}
show_data.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/ip_icmp.h>
#include <netinet/udp.h>
#include <netinet/tcp.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include "sniffer.h"
#include "tools.h"
/* 写 IP 头部到日志文件 */
void print_ip_header(unsigned char *buf, int size, t_sniffer *sniffer)
{
unsigned short iphdrlen;
struct iphdr *iph;
struct sockaddr_in source;
struct sockaddr_in dest;
iph = (struct iphdr *)buf;
iphdrlen = iph->ihl*4;
(void)iphdrlen;
(void)size;
memset(&source, 0, sizeof(source));
source.sin_addr.s_addr = iph->saddr;
memset(&dest, 0, sizeof(dest));
dest.sin_addr.s_addr = iph->daddr;
fprintf(sniffer->logfile,"\n");
fprintf(sniffer->logfile,"IP Header\n");
fprintf(sniffer->logfile," |-IP Version : %d\n",(unsigned int)iph->version);
fprintf(sniffer->logfile," |-IP Header Length : %d DWORDS or %d Bytes\n",(unsigned int)iph->ihl,((unsigned int)(iph->ihl))*4);
fprintf(sniffer->logfile," |-Type Of Service : %d\n",(unsigned int)iph->tos);
fprintf(sniffer->logfile," |-IP Total Length : %d Bytes(size of Packet)\n",ntohs(iph->tot_len));
fprintf(sniffer->logfile," |-Identification : %d\n",ntohs(iph->id));
fprintf(sniffer->logfile," |-TTL : %d\n",(unsigned int)iph->ttl);
fprintf(sniffer->logfile," |-Protocol : %d\n",(unsigned int)iph->protocol);
fprintf(sniffer->logfile," |-Checksum : %d\n",ntohs(iph->check));
fprintf(sniffer->logfile," |-Source IP : %s\n",inet_ntoa(source.sin_addr));
fprintf(sniffer->logfile," |-Destination IP : %s\n",inet_ntoa(dest.sin_addr));
}
/* 写 TCP 数据包到日志文件 */
void print_tcp_packet(unsigned char *buf, int size, t_sniffer *sniffer)
{
unsigned short iphdrlen;
struct iphdr *iph;
struct tcphdr *tcph;
iph = (struct iphdr *)buf;
iphdrlen = iph->ihl * 4;
tcph = (struct tcphdr*)(buf + iphdrlen);
print_ip_header(buf, size, sniffer);
/* 把 tcp 头信息写入日志文件中 */
fprintf(sniffer->logfile,"\n");
fprintf(sniffer->logfile,"TCP Header\n");
fprintf(sniffer->logfile," |-Source Port : %u\n",ntohs(tcph->source));
fprintf(sniffer->logfile," |-Destination Port : %u\n",ntohs(tcph->dest));
fprintf(sniffer->logfile," |-Sequence Number : %u\n",ntohl(tcph->seq));
fprintf(sniffer->logfile," |-Acknowledge Number : %u\n",ntohl(tcph->ack_seq));
fprintf(sniffer->logfile," |-Header Length : %d DWORDS or %d BYTES\n" ,(unsigned int)tcph->doff,(unsigned int)tcph->doff*4);
fprintf(sniffer->logfile," |-Urgent Flag : %d\n",(unsigned int)tcph->urg);
fprintf(sniffer->logfile," |-Acknowledgement Flag : %d\n",(unsigned int)tcph->ack);
fprintf(sniffer->logfile," |-Push Flag : %d\n",(unsigned int)tcph->psh);
fprintf(sniffer->logfile," |-Reset Flag : %d\n",(unsigned int)tcph->rst);
fprintf(sniffer->logfile," |-Synchronise Flag : %d\n",(unsigned int)tcph->syn);
fprintf(sniffer->logfile," |-Finish Flag : %d\n",(unsigned int)tcph->fin);
fprintf(sniffer->logfile," |-Window : %d\n",ntohs(tcph->window));
fprintf(sniffer->logfile," |-Checksum : %d\n",ntohs(tcph->check));
fprintf(sniffer->logfile," |-Urgent Pointer : %d\n",tcph->urg_ptr);
fprintf(sniffer->logfile,"\n");
fprintf(sniffer->logfile," DATA Dump ");
fprintf(sniffer->logfile,"\n");
fprintf(sniffer->logfile,"IP Header\n");
PrintData(buf, iphdrlen, sniffer);
fprintf(sniffer->logfile,"TCP Header\n");
PrintData(buf+iphdrlen, tcph->doff*4, sniffer);
fprintf(sniffer->logfile,"Data Payload\n");
/* 把用户数据写入日志文件 */
PrintData(buf + iphdrlen + tcph->doff*4,
(size - tcph->doff*4-iph->ihl*4),
sniffer );
fprintf(sniffer->logfile,"\n###########################################################");
}
/* 写 UDP 数据包到日志文件 */
void print_udp_packet(unsigned char *buf , int size, t_sniffer *sniffer)
{
unsigned short iphdrlen;
struct iphdr *iph;
struct udphdr *udph;
iph = (struct iphdr *)buf;
iphdrlen = iph->ihl*4;
udph = (struct udphdr*)(buf + iphdrlen);
fprintf(sniffer->logfile,"\n\n***********************UDP Packet*************************\n");
print_ip_header(buf, size, sniffer);
/* 把 udp 头信息写入日志文件中 */
fprintf(sniffer->logfile,"\nUDP Header\n");
fprintf(sniffer->logfile," |-Source Port : %d\n" , ntohs(udph->source));
fprintf(sniffer->logfile," |-Destination Port : %d\n" , ntohs(udph->dest));
fprintf(sniffer->logfile," |-UDP Length : %d\n" , ntohs(udph->len));
fprintf(sniffer->logfile," |-UDP Checksum : %d\n" , ntohs(udph->check));
fprintf(sniffer->logfile,"\n");
fprintf(sniffer->logfile,"IP Header\n");
PrintData(buf , iphdrlen, sniffer);
fprintf(sniffer->logfile,"UDP Header\n");
PrintData(buf+iphdrlen, sizeof(udph), sniffer);
fprintf(sniffer->logfile,"Data Payload\n");
/* 把用户数据写入日志文件 */
PrintData(buf + iphdrlen + sizeof udph,
(size - sizeof udph - iph->ihl * 4),
sniffer);
fprintf(sniffer->logfile,"\n###########################################################");
}
/* 写 ICMP 数据包到日志文件 */
void print_icmp_packet(unsigned char *buf , int size, t_sniffer *sniffer)
{
unsigned short iphdrlen;
struct iphdr *iph;
struct icmphdr *icmph;
iph = (struct iphdr *)buf;
iphdrlen = iph->ihl * 4;
icmph = (struct icmphdr *)(buf + iphdrlen);
/* 把 icmp 头信息写入日志文件中 */
fprintf(sniffer->logfile,"\n\n***********************ICMP Packet*************************\n");
print_ip_header(buf , size, sniffer);
fprintf(sniffer->logfile,"\n");
fprintf(sniffer->logfile,"ICMP Header\n");
fprintf(sniffer->logfile," |-Type : %d",(unsigned int)(icmph->type));
if((unsigned int)(icmph->type) == 11)
fprintf(sniffer->logfile," (TTL Expired)\n");
else if((unsigned int)(icmph->type) == ICMP_ECHOREPLY)
fprintf(sniffer->logfile," (ICMP Echo Reply)\n");
fprintf(sniffer->logfile," |-Code : %d\n",(unsigned int)(icmph->code));
fprintf(sniffer->logfile," |-Checksum : %d\n",ntohs(icmph->checksum));
fprintf(sniffer->logfile,"\n");
fprintf(sniffer->logfile,"IP Header\n");
PrintData(buf, iphdrlen, sniffer);
fprintf(sniffer->logfile,"UDP Header\n");
PrintData(buf + iphdrlen , sizeof(icmph), sniffer);
fprintf(sniffer->logfile,"Data Payload\n");
/* 最后将用户数据写入日志文件中 */
PrintData(buf + iphdrlen + sizeof(icmph),
(size - sizeof(icmph) - iph->ihl * 4),
sniffer);
fprintf(sniffer->logfile,"\n###########################################################");
}
/* 写用户数据到日志文件 */
void PrintData(unsigned char *buf, int size, t_sniffer *sniffer)
{
int i;
for(i = 0 ; i < size ; i++)
{
if(i % 16 == 0)
fprintf(sniffer->logfile, "\n");
fprintf(sniffer->logfile, " %02X",(unsigned int)buf[i]);
if( i == size - 1)
fprintf(sniffer->logfile, "\n");
}
}
main.c
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <errno.h>
#include "sniffer.h"
#include "tools.h"
#define ETH_P_IP 0x0800
int exec_cmd(char *buffer, int len)
{
if (strncmp(buffer, "quit", 4) == 0)
return (1);
return (0);
}
int command_interpreter(int sd)
{
int len;
char buf[512];
len = read(0, buf, 512);
if (len > 0)
{
if (exec_cmd(buf, len) == 1)
return (1);
}
return (0);
}
void display_time_and_date()
{
INITCOLOR(RED_COLOR);
printf("[%s]", __DATE__); /* 打印日期 */
INITCOLOR(GREEN_COLOR);
printf("[%s] ", __TIME__); /* 打印时间 */
INITCOLOR(ZERO_COLOR);
}
void getting_started()
{
CLEARSCREEN(); /* 清空屏幕 */
display_time_and_date();
printf("Getting started of Network sniffer\n\n");
}
/* 主函数入口 */
int main()
{
/* 声明部分 */
int sd;
int res;
int saddr_size;
int data_size;
struct sockaddr saddr;
unsigned char *buffer; /* 保存数据包的数据 */
t_sniffer sniffer; /* 保存数据包的类型和日志文件等信息 */
fd_set fd_read;
buffer = malloc(sizeof(unsigned char *) * 65536);
/* 以可写的方式在当前文件夹中创建日志文件 */
sniffer.logfile = fopen("log.txt", "w");
fprintf(sniffer.logfile,"***LOGFILE(%s - %s)***\n", __DATE__, __TIME__);
if (sniffer.logfile == NULL)
{
perror("fopen(): ");
return (EXIT_FAILURE);
}
sniffer.prot = malloc(sizeof(t_protocol *));
/* 创建原始套接字,ETH_P_ALL 表示侦听负载为 IP 数据报的以太网帧 */
sd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP));
if (sd < 0)
{
perror("socket(): ");
return (EXIT_FAILURE);
}
getting_started();
signal(SIGINT, &signal_white_now);
signal(SIGQUIT, &signal_white_now);
/* 循环侦听以太网帧,并调用 ProcessPacket 函数解析 */
while (1)
{
FD_ZERO(&fd_read);
FD_SET(0, &fd_read);
FD_SET(sd, &fd_read);
/* 多路复用检测可读的套接字和标准输入 */
res = select(sd + 1, &fd_read, NULL, NULL, NULL);
if (res < 0)
{
close(sd);
if (errno != EINTR)
perror("select() ");
return (EXIT_FAILURE);
}
else
{
/* 如果是标准输入可读,进入命令行处理程序 command_interpreter,暂时只支持 'quit' 命令 */
if (FD_ISSET(0, &fd_read))
{
if (command_interpreter(sd) == 1)
break;
}
/* 如果是套接字可读,则读取以太网数据帧的内容,并调用 ProcessPacket 函数解析出数据包的类型 */
else if (FD_ISSET(sd, &fd_read))
{
/* 读取以太网数据帧的内容 */
saddr_size = sizeof(saddr);
data_size = recvfrom(sd, buffer, 65536, 0, &saddr,(socklen_t*)&saddr_size); /* 读取以太网数据帧的内容 */
if (data_size <= 0)
{
close(sd);
perror("recvfrom(): ");
return (EXIT_FAILURE);
}
ProcessPacket(buffer, data_size, &sniffer); /* 调用 ProcessPacket 函数解析出数据包的类型 */
}
}
}
close(sd);
return (EXIT_SUCCESS);
}
void ProcessPacket(unsigned char* buffer, int size, t_sniffer *sniffer)
{
buffer = buffer + 6 + 6 + 2; /* 根据太网帧结构,前 6B 是目的 MAC 地址,接下来的是源 MAC 地址,接下来 2B 是帧长度,其余的是负载(上层的 IP 数据报) */
struct iphdr *iph = (struct iphdr*)buffer;
++sniffer->prot->total; /* 数据包总数加 1 */
/* 根据 TCP/IP 协议规定的 IP 数据报头部的 protocol 字段的值,判断上层的数据包类型 */
switch (iph->protocol)
{
/* 1 表示 icmp 协议 */
case 1:
++sniffer->prot->icmp;
print_icmp_packet(buffer, size, sniffer);
break;
/* 2 表示 igmp 协议 */
case 2:
++sniffer->prot->igmp;
break;
/* 6 表示 tcp 协议 */
case 6:
++sniffer->prot->tcp;
print_tcp_packet(buffer , size, sniffer);
break;
/* 17 表示 udp 协议 */
case 17:
++sniffer->prot->udp;
print_udp_packet(buffer , size, sniffer);
break;
default:
++sniffer->prot->others;
break;
}
display_time_and_date(); /* 显示时间 */
/* 打印 sniffer 中的信息 */
printf("TCP : %d UDP : %d ICMP : %d IGMP : %d Others : %d Total : %d\n",
sniffer->prot->tcp, sniffer->prot->udp,
sniffer->prot->icmp, sniffer->prot->igmp,
sniffer->prot->others, sniffer->prot->total);
}
0x04程序运行
本程序运行界面如下:
实现的功能之一就是解析UDP数据报,其扫描解析结果存放在log.txt中。这里从log.txt中以一个UDP数据包的解析为例
***LOGFILE(Dec 14 2017 - 21:41:38)***
***********************UDP Packet*************************
IP Header
|-IP Version : 4
|-IP Header Length : 5 DWORDS or 20 Bytes
|-Type Of Service : 0
|-IP Total Length : 213 Bytes(size of Packet)
|-Identification : 6639
|-TTL : 64
|-Protocol : 17
|-Checksum : 31927
|-Source IP : 172.16.69.82
|-Destination IP : 172.16.69.255
UDP Header
|-Source Port : 138
|-Destination Port : 138
|-UDP Length : 193
|-UDP Checksum : 26258
IP Header
保护隐私,这部分我删除了
AC 10 45 FF
UDP Header
保护隐私,这部分我删除了
Data Payload
保护隐私,这部分我删除了
###########################################################
0x05附
我认为本实验的亮点在于使用脚本控制,有必要好好学习下脚本编程
launcher.sh
#!/bin/sh
sigint()
{
printf '\nQUIT !\n'
exit 1
}
main()
{
clear
printf "\t\t\t\t\tWelcome to Sniffer Project r\n\n"
while [ 1 ]; do
printf "Select option: \n\n"
printf "1 : Build Project\n"
printf "2 : Launch Project\n"
printf "3 : Remove Object files\n"
printf "4 : Rebuild\n"
printf "0 : Exit\n"
printf "\nYou choose: "
read option
if [ $option = 0 ]
then
exit
fi
if [ $option -ge 1 ] && [ $option -le 4 ]
then
if [ $option = 1 ]
then
make "network_sniffer"
fi
if [ $option = 2 ]
then
"./network_sniffer"
fi
if [ $option = 3 ]
then
make clean
fi
if [ $option = 4 ]
then
make re
fi
else
printf "This option does not exist\n"
fi
done
}
trap 'sigint' 2
main
C语言实现Linux网络嗅探器的更多相关文章
- Linux网络编程&内核学习
c语言: 基础篇 1.<写给大家看的C语言书(第2版)> 原书名: Absolute Beginner's Guide to C (2nd Edition) 原出版社: Sams 作者: ...
- Linux网络编程必看书籍推荐
首先要说讲述计算机网络和TCP/IP的书很多. 先要学习网络知识才谈得上编程 讲述计算机网络的最经典的当属Andrew S.Tanenbaum的<计算机网络>第五版,这本书难易适中. &l ...
- Linux C 程序 Linux网络编程(21)
Linux网络编程网络编程必备的理论基础网络模型,地址,端口,TCP/IP协议 TCP/IP协议是目前世界上使用最广泛的网络通信协议日常中的大部分应用使用该系列协议(浏览网页,收发电子邮件,QQ聊天等 ...
- Linux 网络编程基础(1)--网络相关的数据结构及转化函数
在Linux下进行网络编程,使用的语言一般为C.就个人感受而言,在Linux下进行网络程序的编写,重要的不是代码能力要多强,而是对Linux的网络编程思想的理解和对Linux网络数据结构的掌握.如果想 ...
- 服务器编程入门(4)Linux网络编程基础API
问题聚焦: 这节介绍的不仅是网络编程的几个API 更重要的是,探讨了Linux网络编程基础API与内核中TCP/IP协议族之间的关系. 这节主要介绍三个方面的内容:套接字( ...
- Linux 高性能服务器编程——Linux网络编程基础API
问题聚焦: 这节介绍的不仅是网络编程的几个API 更重要的是,探讨了Linux网络编程基础API与内核中TCP/IP协议族之间的关系. 这节主要介绍三个方面的内容:套接字(so ...
- Linux 网络流量实时监控工具之ntopng详解
大纲一.前言二.ntopng 简介三.ntopng 功能说明 四.ntopng 安装详解五.ntopng 配置详解 六.ntopng 使用详解注,操作系统 CentOS 5.5 X86_64,软件版本 ...
- Linux网络编程经典书籍推荐
UNIX环境高级编程<高级unix环境编程><unix网络编程><深入理解计算机系统>比较好 =====================Linux网络编程经典书籍推 ...
- 网络驱动移植之解析Linux网络驱动的基本框架
内核源码:linux-2.6.38.8.tar.bz2 概括而言,编写Linux网络驱动其实只要完成两件事即可,一是分配并初始化网络设备,二是注册网络设备. 1.分配并初始化网络设备 动态分配网络设备 ...
随机推荐
- CMake生成OpenCV解决方案&&编译OpenCV源码
生成OpenCV工程需要用到CMake,所以第一步需要下载CMake软件,下载链接:CMake下载 目前最新的版本是3.7.1,这里选择下载Platform下的Windows win32-x86 ZI ...
- C++ Primer Plus的若干收获--(十一)
本篇主要讲了转换函数的利与弊以及简要介绍流与iostream文件 因为在本篇仍然要使用上篇的Stonewt类,这里首先给出其代码 <span style="font-size:18px ...
- windows下Redis 主从读写分离部署
原文:windows下Redis 主从读写分离部署 1.可直接下载window下的运行文件(下面这个链接) 也可以浏览github 查看相应的版本说明文档 https://github.com/Ser ...
- Monthly Expense
Problem Description Farmer John is an astounding accounting wizard and has realized he might run out ...
- asp.net (webapi) core 2.1 跨域配置
原文:asp.net (webapi) core 2.1 跨域配置 官方文档 ➡️ https://docs.microsoft.com/zh-cn/aspnet/core/security/cors ...
- nyoj 92 图片实用面积【bfs】
图像实用区域 时间限制:3000 ms | 内存限制:65535 KB 难度:4 描写叙述 "ACKing"同学曾经做一个图像处理的项目时.遇到了一个问题,他须要摘取出图片中某 ...
- WPF - 图形设计器(Diagram Designer)
原文:WPF - 图形设计器(Diagram Designer) OpenExpressApp计划中包括建模工具,计划是采用MetaEdit+模型来作为元模型,使用codeproject的<WP ...
- 读BeautifulSoup官方文档之html树的搜索(2)
除了find()和find_all(), 这里还提供了许多类似的方法我就细讲了, 参数和用法都差不多, 最后四个是next, previous是以.next/previous_element()来说的 ...
- Delphi调试activex
以前好多次遇到了activex无法调试的问题,一直没搞清楚原因,最近终于搞清楚了,原来是IE由单线程变成了多线程. 下面就说说调试activex的方法 一.简单的方式,这种方式只适用于浏览器为单线程的 ...
- WPF/Silverlight 页面绑定Model验证机制升级版
关于WPF/Silverlight的数据验证,想必大家都不陌生了. 各大牛的博客里都不泛对这方面讨论的文章. 个人比较赞赏 JV9的“Silverlight实例教程”里的Validation数据验证. ...