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 ...
随机推荐
- highCharts使用记录
公司的架构师让我做一个mockup,要用到highCharts,,以前想接触的,没时间学习,也没有用过,正好工作可以用上了,可以边学边做了. 环境 <script src="./js/ ...
- 另类之将ipython notebook嵌入blog方法
另类之将ipython notebook嵌入blog方法 ipynb文件很强大. 可是一直苦于没有找到好的方法把它直接嵌入到博文里. 现在得到一个另类的方法: 就是利用github集成了nbviewe ...
- Arcgis10.1 Arcobject连接Oracel数据库
原来使用Arcgis9.3的版本,现在升级到了10.1遇到不少问题,原来初始化工作空间的代码无法正常运行了,修改后的代码如下: static void Test() { IPropertySet sd ...
- HDU 6096 String (AC自动机)
题目链接 Problem Description Bob has a dictionary with N words in it. Now there is a list of words in wh ...
- php Only variables should be passed by reference 报错问题
这个错误是变量引用引起的非致命错误,可修改php.ini文件的error_reporting = E_ALL & E_NOTICE 使其屏蔽此错误
- 树形dp(B - Computer HDU - 2196 )
题目链接:https://cn.vjudge.net/contest/277955#problem/B 题目大意:首先输入n代表有n个电脑,然后再输入n-1行,每一行输入两个数,t1,t2.代表第(i ...
- equals方法变量和常量位置区别
对于字符串比较,我的习惯用法是 变量.equals(常量) 比如: a.equals("a") 今天看视频才知道变量在前面与后面有很大影响,正确的写法是常量放前面(可以 ...
- aarch64_g2
ghc-cryptonite-devel-0.21-1.fc26.aarch64.rpm 2017-02-28 01:28 3.1M fedora Mirroring Project ghc-css- ...
- c# List 所有操作方法例子
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Ref ...
- Python基础:内置类型(未完待续)
本文根据Python 3.6.5的官文Built-in Types而写. 目录 1.真值测试 2.布尔操作 -- and, or, not 3.比较 4.数字型 -- int, float, comp ...