2017-2018-1 20179202《Linux内核原理与分析》第十二周作业
C语言实现Linux网络嗅探器
一、知识准备
1.一般情况下,网络上所有的机器都可以“听”到通过的流量,但对不属于自己的数据包则不予响应。如果某个工作站的网络接口处于混杂模式,那么它就可以捕获网络上所有的数据包和帧。
2.为了绕过标准的TCP/IP堆栈,网卡就必须设置为混杂模式。一般情况下,要激活这种方式,内核需要root权限来运行这种程序,所以Sniffer需要root身份安装。
3.当一个黑客成功地攻陷了一台主机,并拿到了root权限,而且还想利用这台主机去攻击同一网段上的其他主机时,就会在这台主机上安装Sniffer软件,对以太网设备上传送的数据包进行侦听,从而发现感兴趣的包。
4.socket套接字用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
5.网络嗅探器是拦截通过网络接口流入和流出的数据的程序。在本实验中用C语言实现一个网络嗅探器。程序框架和功能描述如图所示:
二、源码分析
1.sniffer.h
该头文件定义了s_protocol和s_sniffer两个结构体,并声明了把IP头部、TCP数据包、UDP数据包、ICMP数据包及用户包数据写到日志文件的函数、解析出数据包类型的函数(ProcessPacket)等:
#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
2.main.c
该文件包含main函数,创建套接字侦听,解析数据包,将解析结果写入log.txt中:
#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_IP 表示侦听负载为 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);
}
3.show_data.c
该文件就是往log.txt中写入数据包信息:
#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");
}
}
4.tools.h和tools.c定义了颜色、信号处理函数:
#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
#include <signal.h>
#include <stdio.h>
void signal_white_now(int signum)
{
printf("Bye Bye !\n");
}
三、实验截图
1.运行结果
2.log.txt中的一个UDP数据包
四、题外话
源码中有一个.sh文件,不知是什么?查资料后知道是shell脚本文件,shell脚本就是一些命令的集合。如果实现这样的操作:
- 进入到/tmp/目录;
- 列出当前目录中所有的文件名;
- 把所有当前的文件拷贝到/root/目录下;
- 删除当前目录下所有的文件。
简单的4步在shell窗口中需要敲4次命令,按4次回车。这样是不是很麻烦?当然这4步操作非常简单,如果是更加复杂的命令设置需要几十次操作呢?那样的话一次一次敲键盘会很麻烦。所以不妨把所有的操作都记录到一个文档中,然后去调用文档中的命令,这样一步操作就可以完成。这个文档就是shell脚本。shell脚本能很方便的去管理服务器,因为可以指定一个任务计划定时去执行某一个shell脚本实现需求。
2017-2018-1 20179202《Linux内核原理与分析》第十二周作业的更多相关文章
- 2018-2019-1 20189221 《Linux内核原理与分析》第八周作业
2018-2019-1 20189221 <Linux内核原理与分析>第八周作业 实验七 编译链接过程 gcc –e –o hello.cpp hello.c / gcc -x cpp-o ...
- 2018-2019-1 20189221 《Linux内核原理与分析》第七周作业
2018-2019-1 20189221 <Linux内核原理与分析>第七周作业 实验六 分析Linux内核创建一个新进程的过程 代码分析 task_struct: struct task ...
- 2018-2019-1 20189221 《Linux内核原理与分析》第六周作业
2018-2019-1 20189221 <Linux内核原理与分析>第六周作业 实验五 实验过程 将Fork函数移植到Linux的MenuOS fork()函数通过系统调用创建一个与原来 ...
- 2018-2019-1 20189221《Linux内核原理与分析》第五周作业
2018-2019-1 20189221<Linux内核原理与分析>第五周作业 实验四 实验过程 当用户态进程调用一个系统调用时,cpu切换到内核态并开始执行一个内核函数. 在Linux中 ...
- 2018-2019-1 20189221《Linux内核原理与分析》第三周作业
2018-2019-1 20189221<Linux内核原理与分析>第三周作业 实验二 完成一个简单的时间片轮转多道程序内核代码 实验过程 在实验楼中编译内核 编写mymain.c函数和m ...
- 2019-2020-1 20199329《Linux内核原理与分析》第十三周作业
<Linux内核原理与分析>第十三周作业 一.本周内容概述 通过重现缓冲区溢出攻击来理解漏洞 二.本周学习内容 1.实验简介 注意:实验中命令在 xfce 终端中输入,前面有 $ 的内容为 ...
- 2019-2020-1 20199329《Linux内核原理与分析》第十一周作业
<Linux内核原理与分析>第十一周作业 一.本周内容概述: 学习linux安全防护方面的知识 完成实验楼上的<ShellShock 攻击实验> 二.本周学习内容: 1.学习& ...
- 2019-2020-1 20199329《Linux内核原理与分析》第八周作业
<Linux内核原理与分析>第八周作业 一.本周内容概述: 理解编译链接的过程和ELF可执行文件格式 编程练习动态链接库的两种使用方式 使用gdb跟踪分析一个execve系统调用内核处理函 ...
- 2019-2020-1 20199329《Linux内核原理与分析》第七周作业
<Linux内核原理与分析>第七周作业 一.本周内容概述: 对Linux系统如何创建一个新进程进行追踪 分析Linux内核创建一个新进程的过程 二.本周学习内容: 1.学习进程的描述 操作 ...
- 2019-2020-1 20199329《Linux内核原理与分析》第六周作业
<Linux内核原理与分析>第六周作业 一.本周内容概述: 学习系统调用的相关理论知识,并使用库函数API和C代码中嵌入汇编代码两种方式使用getpid()系统调用 学习系统调用syste ...
随机推荐
- NGINX配置PHP解析
<?php phpinfo(); ?> location ~ \.php$ { root html; fastcgi_pass ; fastcgi_index index.php; fas ...
- [Luogu 2169] 正则表达式
[Luogu 2169] 正则表达式 感谢 0xis 推题. 记忆中很久没有过一遍写过一题了- 别被题目名称蒙骗!这不是正则表达式题目!和字符(串)处理一点关系都没有!这是个图论题啊喂! 题都没急,C ...
- Redis学习一:Nosql入门和概述
现在Redis越来越火,为了适应技术的发展,开始学习一下Redis,在学习Redis之前先学习一下Nosql. 第一部分:入门概述 1.1 互联网时代背景下大机遇,为什么用nosql 1.1.1 单机 ...
- SVN自动更新-win平台
把项目给外包做,他们天天整个ftp传来传去,上传一次还要到处翻View和Controller,还有漏传的情况,简直low到不行.看不下去了,就准备整个svn.虽然svn解决了上传的问题,但是自动发布还 ...
- 20155306 2016-2017-2 《Java程序设计》第6周学习总结
20155306 2016-2017-2 <Java程序设计>第6周学习总结 教材学习内容总结 第十章 输入/输出 10.1 InputStream与OutputStream 如果要将数据 ...
- [CodePlus 2017 11月赛&洛谷P4058]木材 题解(二分答案)
[CodePlus 2017 11月赛&洛谷P4058]木材 Description 有 n棵树,初始时每棵树的高度为 Hi ,第 i棵树每月都会长高 Ai.现在有个木料长度总量为 S的订单, ...
- PHPCMS v9 安全防范教程!
一.目录权限设置很重要:可以有效防范黑客上传木马文件.如果通过 chmod 644 * -R 的话,php文件就没有权限访问了.如果通过chmod 755 * -R 的话,php文件的权限就高了. 所 ...
- [转]QList内存释放
QList<T> 的释放分两种情况: 1.T的类型为非指针,这时候直接调用clear()方法就可以释放了,看如下测试代码 #include <QtCore/QCoreApplicat ...
- vuejs心法和技法
原文地址:http://www.cnblogs.com/kidsitcn/p/5409994.html 所有的vuejs组件都是被扩展的vue实例: var MyComponent = Vue.ext ...
- 利用正则表达式去除所有html标签,只保留文字
后台将富文本编辑器中的内容返回到前端时如果带上了标签,这时就可以利用这种方法只保留文字. 标签的格式有以下几种 1.<div class="test"></div ...