第四周课上实践验收

目录

  • 任务一:基于winpcap的网络嗅探工具的实现

    • 1 实践原理介绍
    • 2 实践环境搭建
    • 3 实现过程
    • 4 实验截图
  • 任务二:嗅探登录网站的账号和密码
  • 任务三:抓取手机App登录过程数据包
  • 实践思考
  • 参考资料

任务一:基于winpcap的网络嗅探工具的实现

1 实践原理介绍

Windows用户态中的winpcap与Unix中的libpcap库相兼容,使用winpcap库实现网络嗅探工具的原理与Unix下使用libpcap非常的类似,具体的实现技术如下图:

根据上面的技术图可知嗅探器的实现基本的流程是检测网卡->选择网卡->过滤器的设置(可以设置需要捕捉的协议包)->数据包分析

2 实践环境搭建

  • Step 1 实践软件的准备

    首先需要下载winpcap的两个东西:

    (1)winpcap V4.1.2 驱动程序、dll文件:http://www.winpcap.org/install/bin/WinPcap_4_1_2.exe

    (2)winpcap V4.1.2 Developr‘s Pack,库文件、头文件、简单的示例程序代码和帮助文件:http://www.winpcap.org/install/bin/WpdPack_4_1_2.zip

    (3)所有的配置文件我分享在百度网盘中:链接:https://pan.baidu.com/s/14mfm1BEr1vdc_kchzlapTw 提取码:jwrc

  • Step 2 打开Dev-C++->新建项目(一般选择C++,Dev-C++这个软件也能够很好地兼容C)

  • Step 3 配置项目环境

    (1)Dev-C++导航栏工具->编译选项->设定编译器配置->目录->C/C++包含文件->点击右下角文件图标添加文件目录->某盘\WpdPack\Include->确定

    (2)Dev-C++导航栏项目->项目属性->参数->链接->加入库或者对象->某盘\WpdPack\Lib\wpcap.lib和某盘\Microsoft SDKs\Windows\v7.0A\Lib\WS2_32.Lib ->确定

    (3)代码中加入语句#define HAVE_REMOTE#define WPCAP#include "winsock.h"(说明:have_remote是嗅探器的远程捕获,wpcap是winpcap文件,winsock.h是windows下的sock编程头文件,socket编程可以自行百度)

    至此整个环境就配置完成了。

3 实现过程

首先给出winpcap中文技术文档,这个文档中有winpcap的模块、数据结构、头文件列表等,里面的代码都是开源的,可以按照模块中的文件步骤一步步下来就能实现简单的嗅探器。

  • Step 1 获取已连接的网络适配器列表

libpcap和WinPcap都提供了 pcap_findalldevs_ex() 函数来实现这个功能: 这个函数返回一个 pcap_if 结构的链表每个这样的结构都包含了一个适配器的详细信息。值得注意的是,数据域 namedescription 表示一个适配器名称和一个可以让人们理解的描述。

下面上代码获取适配器列表

	pcap_if_t * alldevs, *device;
	int i = 0;
	int iNum;
	pcap_t * adhandle;
	char errbuf[PCAP_ERRBUF_SIZE];
	//修改这里可以更改捕获的数据包使用的协议类型
    char packet_filter[] = "(ip and udp) or (ip and tcp) or (ip and icmp)"; 

	if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)
	{ //获取设备列表
		fprintf(stderr,"无法打开网络设备:%s\n", errbuf);
		return 1;
	}
	for (device = alldevs; device  != NULL; device = device->next)
	{ //打印列表
		if (i == 0)
		{
			printf("请按CTRL + C退出!\n\n");
			printf("网络设备如下:\n");
		}
		printf("%d. %s\n", ++i, device -> name);
		if (device->description)
			printf(" (%s)\n", device->description);
		else
			printf("没有设备描述信息!");
	}
	if (i == 0)
	{
		printf("\n请先安装WinPcap!");
		return -1;
	}

说明:

(1) pcap_findalldevs_ex() ,和其他libpcap函数一样,有一个 errbuf 参数。一旦发生错误,这个参数将会被libpcap写入字符串类型的错误信息;

(2)不是所有的操作系统都支持libpcap提供的网络程序接口,因此,如果我们想编写一个可移植的应用程序,我们就必须考虑在什么情况下, description 是 null。本程序中,我们遇到这种情况时,会打印提示语句"没有设备描述信息!";

(3)当我们完成了设备列表的使用,我们要调用 pcap_freealldevs() 函数将其占用的内存资源释放。

(4)pcap_if_t数据结构有五个属性,名字、描述、pcap_addr类型的地址( pacp_addr 同样有五个属性,ip地址、子网掩码、广播地址、目标地址)、flags、与指向下一个元素的指针。

  • Step 2 选择网络设备接口
	printf("请选择网络设备接口:(1 - %d):", i);
	scanf("%d", &iNum);
	if (iNum < 1 || iNum > i)
	{
		printf("设备不存在!\n");
		pcap_freealldevs(alldevs);
		return -1;
	}
	//跳转到已选设备
    for (device = alldevs, i = 0;i < iNum -1 ; device = device -> next,i++);

说明:可以打开联网主机的网络适配器查看网卡有哪些,正在联网的网卡是哪个,下面是我的主机的联网情况(这个代码不能识别出无线wifi的网卡,有点疑惑这个地方,想了下可能是因为wifi和以太网是属于两种链路层网络的协议所以代码的实现不同,网上查了下也没找到解决办法)

  • Step 3 打开适配器

打开设备的函数是 pcap_open()。下面是参数 snaplen, flagsto_ms 的解释说明:

snaplen 用来指定要捕获数据包中的哪些部分。本例中,我们将值定为65535,它比我们能遇到的最大的MTU还要大。因此,能够收到完整的数据包。

flags: 最最重要的flag是用来指示适配器是否要被设置成混杂模式一般情况下,适配器只接收发给它自己的数据包, 而那些在其他机器之间通讯的数据包,将会被丢弃。 相反,如果适配器是混杂模式,那么不管这个数据包是不是发给我的,都会去捕获。 这意味着在一个共享媒介(比如总线型以太网),WinPcap能捕获其他主机的所有的数据包。 大多数用于数据捕获的应用程序都会将适配器设置成混杂模式,所以,这个代码中也使用混杂模式。

to_ms 指定读取数据的超时时间,以毫秒计(1s=1000ms)。在适配器上进行读取操作(比如用 pcap_dispatch()pcap_next_ex()) 都会在 to_ms 毫秒时间内响应,即使在网络上没有可用的数据包。 在统计模式下,to_ms 还可以用来定义统计的时间间隔。 将 to_ms 设置为0意味着没有超时,那么如果没有数据包到达的话,读操作将永远不会返回。 如果设置成-1,则情况恰好相反,无论有没有数据包到达,读操作都会立即返回。

上代码

    // 打开适配器
    if ( (adhandle= pcap_open(device->name,  // 设备名
                             65536,     // 要捕捉的数据包的部分
                                        // 65535保证能捕获到不同数据链路层上的每个数据包的全部内容
                             PCAP_OPENFLAG_PROMISCUOUS,         // 混杂模式
                             1000,      // 读取超时时间
                             NULL,      // 远程机器验证
                             errbuf     // 错误缓冲池
                             ) ) == NULL)
    {
        fprintf(stderr,"\n不能打开适配器!\n");
        /* 释放设备列表 */
        pcap_freealldevs(alldevs);
        return -1;
    }
  • Step 4 过滤数据包

WinPcap和Libpcap的最强大的特性之一,是拥有过滤数据包的引擎。 它提供了有效的方法去获取网络中的某些数据包,这也是WinPcap捕获机制中的一个组成部分。 用来过滤数据包的函数是 pcap_compile()pcap_setfilter()

pcap_compile() 它将一个高层的布尔过滤表达式编译成一个能够被过滤引擎所解释的低层的字节码。有关布尔过滤表达式的语法可以参见 Filtering expression syntax

pcap_setfilter() 将一个过滤器与内核捕获会话向关联。当 pcap_setfilter() 被调用时,这个过滤器将被应用到来自网络的所有数据包,并且,所有的符合要求的数据包 (即那些经过过滤器以后,布尔表达式为真的包) ,将会立即复制给应用程序。

代码:

	if (device->addresses != NULL) //获得接口第一个地址的掩码
		netmask = ((struct sockaddr_in *)(device->addresses->netmask))->sin_addr.S_un.S_addr;
	else //如果接口没有地址,那么我们假设一个C类的掩码
		netmask = 0xffff00;
	if (pcap_compile(adhandle, &fcode, packet_filter, 1, netmask) < 0)
	{ //编译过滤器 packet_filter为过滤器需要过滤的协议数据类型
		fprintf(stderr, "不能监听过滤该数据报!\n");
		pcap_freealldevs(alldevs);
		return -1;
	}

	if (pcap_setfilter(adhandle, &fcode) < 0)
	{ //设置过滤器
		fprintf(stderr, "过滤设置错误!\n");
		pcap_freealldevs(alldevs);
		return -1;
	}

说明:

(1)代码展示了如何编译并设置过滤器。 请注意,我们必须从 pcap_if 结构体中获得掩码,因为一些使用 pcap_compile() 创建的过滤器需要它。

(2)在这段代码片断中,传递给 pcap_compile() 的过滤器是char packet_filter[] = "(ip and udp) or (ip and tcp) or (ip and icmp)";,这说明我们可以获取IP、TCP、ICMP和UDP的数据包,并把他们发送给应用程序。

(3)struct sockaddr_in {

   short int sin_family;

   unsigned short int sin_port;

   struct in_addr sin_addr;

   unsigned char sin_zero[8];

   };

  sin_family:指代协议族,可取值如下:

        AF_INET    2 IPv4

        AF_INET6   23 IPv6

        AF_UNSPEC  0 协议无关

  sin_port:存储端口号(使用网络字节顺序)

  sin_addr:存储IP地址,使用in_addr这个数据结构

  sin_zero:是为了让sockaddrsockaddr_in两个数据结构保持大小相同而保留的空字节。

? IP地址的定义:

? typedef struct in_addr {

   union {

   struct{ unsigned char s_b1,s_b2, s_b3,s_b4;} S_un_b;

   struct{ unsigned short s_w1, s_w2;} S_un_w;

   unsigned long S_addr;

   } S_un;

   } IN_ADDR;

? in_addr:它是一个存储ip地址的共用体,有三种表达方式:第一种用四个字节来表示IP地址的四个数字;

第二种用两个双字节来表示IP地址;第三种用一个长整型来表示IP地址。

  • Step 5 分析数据

(1)首先看下TCP、IP、UDP等协议的数据结构定义

typedef struct ip_address
{ //ip地址
    u_char b1;
    u_char b2;
    u_char b3;
    u_char b4;
} ip_address;

typedef struct mac_address
{//mac地址
	u_char b1;
	u_char b2;
	u_char b3;
	u_char b4;
	u_char b5;
	u_char b6;
} mac_address;

typedef struct ethe_header
{ //mac帧首部
	mac_address mac_dest_address;//目的地址
	mac_address mac_source_address;//源地址
	u_short ether_type;//以太网MAC帧的格式分为DIX EthernetII和IEEE的802.3标准
} ethe_header;

typedef struct ip_header
{ //ip地址首部
    u_char  ver_ihl;        // 版本 (4 bits) + 首部长度 (4 bits)
    u_char  tos;            // 服务类型(Type of service)
    u_short tlen;           // 总长(Total length)
    u_short identification; // 标识(Identification)
    u_short flags_fo;       // 标志位(Flags) (3 bits) + 段偏移量(Fragment offset) (13 bits)
    u_char  ttl;            // 存活时间(Time to live)
    u_char  proto;          // 协议(Protocol)
    u_short crc;            // 首部校验和(Header checksum)
    ip_address  saddr;      // 源地址(Source address)
    ip_address  daddr;      // 目的地址(Destination address)
    u_int   op_pad;         // 选项与填充(Option + Padding)
} ip_header;

typedef struct udp_header
{ //UPD首部
    u_short sport;          // 源端口(Source port)
    u_short dport;          // 目的端口(Destination port)
    u_short len;            // UDP数据包长度(Datagram length)
    u_short crc;            // 校验和(Checksum)
} udp_header;

typedef struct tcp_header
{ //TCP首部
	u_short sport;          // 源端口(16位)
    u_short dport;          // 目的端口(16位)
    u_int num;              // 序列号 (32位)
	u_int ack;              // 确认号(32位)
	u_short sum;            // 数据偏移(4位),保留(6位),标志位(6位)
	u_short windonw;        // 窗口 (16位)
	u_short crc;            // 检验和 (16位)
	u_short ugr;            // 紧急指针(16位)
} tcp_header;

(2)使用函数pcap_loop(adhandle, 0, packet_handler, NULL); 开始捕捉。其中packet_handler为回调函数,当收到每一个数据包时会被libpcap所调用

void packet_handler(u_char *dumpfile, const struct pcap_pkthdr *header, const u_char *pkt_data)
{ //回调函数,当收到每一个数据包时会被libpcap所调用
	if(header->caplen>400) return;
    struct tm *ltime;
    char timestr[16];
	ip_header * ip_hd;
	udp_header * udp_hd;
	tcp_header * tcp_hd;
    ethe_header * ethe_hd;
	int ip_len,tcp_len,start;
	u_short sport,dport;

	printf("\n");
    ltime=localtime(&header->ts.tv_sec); //将时间戳转换为可读字符
    strftime( timestr, sizeof timestr, "%H:%M:%S", ltime);
    printf("时间:%s\n",timestr);

    ethe_hd = (ethe_header *)pkt_data;
    ip_hd = (ip_header *)(pkt_data + 14);//获得IP数据包头部位置
	/*获得UDP首部位置*/
	ip_len = (ip_hd ->ver_ihl & 0xf) * 4; //ip首部长度
	udp_hd = (udp_header *)((u_char *)ip_hd + ip_len);
	/*将网络字节序列转换成主机字节序列*/
	sport = ntohs(udp_hd->sport);
	dport = ntohs(udp_hd->dport);

	if(ip_hd->proto==17)
	{
		printf("协议:UDP");
		start=ip_len+8; //在IP长度基础上加8字节UDP头移动到UDP数据部分
	}
	else if(ip_hd->proto==6)
	{
		printf("协议:TCP");
		tcp_hd = (tcp_header *)((u_char *)ip_hd + ip_len);
		tcp_len=ntohs(tcp_hd->sum)>>12;
		start=ip_len+tcp_len*4;
	}
	else if(ip_hd->proto==1)
	{
		printf("协议:ICMP");
		start=ip_len+23;
	}
	else printf("协议:其他");
	//printf("start=%d\n",start);
	printf("                      数据报的长度:%d\n",header->caplen);
    //打印IP地址和端口
    printf("源IP地址: %d.%d.%d.%d:%d      目的IP地址:%d.%d.%d.%d:%d\n源端口:%d                     目的端口:%d\n源物理地址: %x-%x-%x-%x-%x-%x   目的物理地址:%x-%x-%x-%x-%x-%x\n",
		  ip_hd->saddr.b1, ip_hd->saddr.b2, ip_hd->saddr.b3, ip_hd->saddr.b4,
		           ip_hd->daddr.b1, ip_hd->daddr.b2, ip_hd->daddr.b3, ip_hd->daddr.b4, sport, dport,
				   ethe_hd->mac_source_address.b1, ethe_hd->mac_source_address.b2, ethe_hd->mac_source_address.b3,
				   ethe_hd->mac_source_address.b4, ethe_hd->mac_source_address.b5, ethe_hd->mac_source_address.b6,
				   ethe_hd->mac_dest_address.b1, ethe_hd->mac_dest_address.b2, ethe_hd->mac_dest_address.b3,
				   ethe_hd->mac_dest_address.b4, ethe_hd->mac_dest_address.b5, ethe_hd->mac_dest_address.b6);
	//输出数据部分
    printf("数据部分内容为:\n");
    for (int i=start; (i < header->caplen + 1 ) ; i++)
    {
        printf("%.2x ", pkt_data[i-1]); //也可以改为 %c 以 ascii码形式输出。
        if ( (i % LINE_LEN) == 0) printf("\n");
    }
    printf("\n\n");
}

说明:

(1) ntohs() 简述: 将一个无符号短整形数从网络字节顺序转换为主机字节顺序(关于主机字节顺序和网络字节顺序可详见博客)。

? #include <winsock.h>

? u_short PASCAL FAR ntohs( u_short netshort);

? netshort:一个以网络字节顺序表达的16位数。

? 返回值:ntohs()返回一个以主机字节顺序表达的数。

(2)注意输出各协议数据的时候需要移动指针到各层对应的应用数据部分,即跳过各层协议的数据头部定义,具体需要移动多少位,需要参看各层的数据包详细结构,这里不做赘述。

4 实验截图

  • (1)检测到的网卡有:

  • (2)捕获的UDP数据包:

  • (3)捕获的TCP数据包:

任务二:嗅探登录网站的账号和密码

Step 1 打开孙启龙同学分享的网站 http://49.233.180.160/jsptest.jsp

说明:现在很多网站都是密文传输,这个网站呢是明文传输并且很好捕获,感谢孙启龙同学的分享让我的第二个任务完成的比较快。

Step 2 打开wireshark开始抓包

Step 3 网站上输入账号密码

Step 4 过滤wireshark中的http数据包查找捕获到的账号和密码

由上图可知我的账号是JodyLZL,密码是123456

任务三:抓取手机App登录过程数据包

Step 1 打开手机热点让电脑和手机处于一个网络中

Step 2 打开wireshark开始抓包

Step 3 电脑上登录百度网盘输入账号密码

Step 4 过滤http数据包查找是否捕获到用户账号和密码

说明:百度网盘这个是密文传输的数据,所以捕获到的是一串乱码的东西,看网上说是MD5加密,又之前上网络信任体系中学到了OpenSSL这个密码库,本来想说用OpenSSL库的MD5加密试试验证下,但是想了下自己没有秘钥...所以最后捕获到的还是一串乱码...

实践思考

感觉实践像买彩票一样,可能运气好的时候呢,一下子就能做完,运气不好的时候呢,可能要做好几天(也可能是我操作太烂)。总的来说,这次实践收获最大的还是网络嗅探器的实现,最开始也是在网上各种的找资料,后来找到了winpcap的中文技术文档,我彩票中了!这写的也太好了,找了些配置博客下载了一些文件很快就能捕获数据了,但是其中查的资料也很多,协议数据包的格式定义,winpcap数据结构和数据类型等都需要考虑,还调试了我的网卡设置...反正搞了一堆,也学了不少(希望我忘记的也很少...)网络攻防真的是很多门课程的综合,需要学习和实践的东西真的很多。

参考资料

原文地址:https://www.cnblogs.com/Jody9123/p/12585362.html

时间: 2024-11-07 03:46:10

第四周课上实践验收的相关文章

2017-2018-1 《信息安全系统设计基础》 20155322 十六周课上实践

2017-2018-1 <信息安全系统设计基础> 20155322 十六周 课上实践 题目 原理 实现 小时设置及提取 #define TIME_Addr 0xFFFFC0000 //实时钟芯片的IO映像基址是OxFFFFC0000 #define TIME *(volatile int *) (TIME_Addr+2) //时间存放在基址+2的寄存器中 void SetHours(int hours) { int oldtime = TIME; int newtime = oldtime &

补做课上实践题目

补做课上实践题目:嵌入式基础 题目 以课上的小时为例: 需要设置小时,首先需要将原来的小时清除,原来的小时有5位,故需要将前五位异或上零. newtime=oldtime&~(0x1F<<11); 还需要放置新的小时时间,将新的小时时间变为5位,然后放置到之前清空的五位上. newtime |= (hours & 0x1F) << 11; 获取小时时间时,直接取小时的五位然后输出即可. Hours=time>>11)&0x1F; return H

20165204Java第四周课上补做

20165204Java第四周课上补做 反省 课下没有认真理解JDB的具体使用过程,只是在按着老师的博客一步一步进行,而没有真正搞懂这些命令行的作用是什么. 在运行在命令行中赋值的程序时没有搞懂,绕了很多弯路. 在进入方法之后,只是使用了step在一步一步地向前推进,而并未使用next,浪费了非常多的时间. 总体来说还是自己课下练习的不认真,导致了课上的不熟练,进而没有完成课上任务. 补做截图 原文地址:https://www.cnblogs.com/jph596299009/p/8643093

20165212任胤第四周课上作业补做

20165212任胤 第四周课上测试补做 题目:jdb调试递归循环程序 程序代码: import java.util.Arrays; public class B { public static void main(String [] args) { if(args.length < 1){ System.out.println("Usage: java CLSumRecursion num1 num2 ..."); System.exit(0); } int m = Integ

20165305 苏振龙《Java程序设计》第四周课上测试补做

第一次测试 第二次测试 第三次测试 上传代码 第四次测试 总结 之前我一直在git bash进行程序设计,但是对于我来说操作起来有点困难,所以我改用了虚拟机,之后之前一直困扰我的问题在虚拟机下就没有了,而且一些·不要能进行的操作例如"tree","jdb调试"等都可以顺利进行.在上一周的课上试验中因为在用git bash运行代码时总是出现不识别的地方,并且也一直没有很好的解决,在以后的学习中我会多留意一些,看是否能找到解决方法. 原文地址:https://www.c

课堂作业第四周课上作业一

题目要求 查找list[]中的最大值:int Largest(int list[], int length); n n首份实现代码如下: int Largest(int list[], int length) { int i,max; for(i = 0; i < (length – 1); i ++ ) { if(list[i] > max) { max=list[i]; } } return max; } 编写一个程序对Largest函数进行测试,并将你的所有的测试用例写在纸上. 代码 #

20165339第四周课上作业补交

一.书上代码运行截图 递归 JDB 二.知识点 Ctrl+Shift+T打开三个标签 stop in <class id>.<method>在方法中设置断点 stop at <class id>:<line>在行中设置断点 clear列出断点 clear <class id>.<method>清除方法中的断点 clear <class id>:<line>清除行中的断点 use [source file pat

20165337第四周课上补做

p29页测试 JDB调试 书上的题 https://gitee.com/BESTI-IS-JAVA-2018/20165337/tree/master/src 原文地址:https://www.cnblogs.com/y963976867/p/8646979.html

课堂作业第四周课上作业二

题目要求:返回一个整数数组中最大子数组的和. 要求: 要求程序必须能处理1000 个元素: 每个元素是int32 类型的: 输入一个整形数组,数组里有正数也有负数. 数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和. 求所有子数组的和的最大值.要求时间复杂度为O(n) 我的伙伴是侯涛亮,我负责程序分析,代码编程,他主要负责代码复审和代码测试计划 工作照: 设计思路:用随机数产生固定的个数为n,其中有负有正,for循环循环N次,默认最大子数组和为零,从a[0]开始使数组的每一个相加为