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)等:

  1. #ifndef __SNIFFER_H__
  2. #define __SNIFFER_H__
  3. typedef struct s_protocol
  4. {
  5. int tcp;
  6. int udp;
  7. int icmp;
  8. int igmp;
  9. int others;
  10. int total;
  11. } t_protocol;
  12. typedef struct s_sniffer
  13. {
  14. FILE *logfile; /* 日志文件 */
  15. t_protocol *prot; /* 数据包协议类型 */
  16. } t_sniffer;
  17. void ProcessPacket(unsigned char*, int, t_sniffer *);
  18. void print_ip_header(unsigned char* , int, t_sniffer *);
  19. void print_tcp_packet(unsigned char* , int, t_sniffer *);
  20. void print_udp_packet(unsigned char * , int, t_sniffer *);
  21. void print_icmp_packet(unsigned char* , int, t_sniffer *);
  22. void PrintData (unsigned char* , int, t_sniffer *);
  23. void display_time_and_date();
  24. void getting_started();
  25. void signal_white_now(int);
  26. #endif

2.main.c

该文件包含main函数,创建套接字侦听,解析数据包,将解析结果写入log.txt中:

  1. #include <signal.h>
  2. #include <unistd.h>
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include <netinet/ip.h>
  7. #include <sys/socket.h>
  8. #include <sys/select.h>
  9. #include <fcntl.h>
  10. #include <sys/types.h>
  11. #include <sys/time.h>
  12. #include <errno.h>
  13. #include "sniffer.h"
  14. #include "tools.h"
  15. #define ETH_P_IP 0x0800
  16. int exec_cmd(char *buffer, int len)
  17. {
  18. if (strncmp(buffer, "quit", 4) == 0)
  19. return (1);
  20. return (0);
  21. }
  22. int command_interpreter(int sd)
  23. {
  24. int len;
  25. char buf[512];
  26. len = read(0, buf, 512);
  27. if (len > 0)
  28. {
  29. if (exec_cmd(buf, len) == 1)
  30. return (1);
  31. }
  32. return (0);
  33. }
  34. void display_time_and_date()
  35. {
  36. INITCOLOR(RED_COLOR);
  37. printf("[%s]", __DATE__); /* 打印日期 */
  38. INITCOLOR(GREEN_COLOR);
  39. printf("[%s] ", __TIME__); /* 打印时间 */
  40. INITCOLOR(ZERO_COLOR);
  41. }
  42. void getting_started()
  43. {
  44. CLEARSCREEN(); /* 清空屏幕 */
  45. display_time_and_date();
  46. printf("Getting started of Network sniffer\n\n");
  47. }
  48. int main()
  49. {
  50. int sd;
  51. int res;
  52. int saddr_size;
  53. int data_size;
  54. struct sockaddr saddr;
  55. unsigned char *buffer; /* 保存数据包的数据 */
  56. t_sniffer sniffer; /* 保存数据包的类型和日志文件等信息 */
  57. fd_set fd_read;
  58. buffer = malloc(sizeof(unsigned char *) * 65536);
  59. /* 以可写的方式在当前文件夹中创建日志文件 */
  60. sniffer.logfile = fopen("log.txt", "w");
  61. fprintf(sniffer.logfile,"***LOGFILE(%s - %s)***\n", __DATE__, __TIME__);
  62. if (sniffer.logfile == NULL)
  63. {
  64. perror("fopen(): ");
  65. return (EXIT_FAILURE);
  66. }
  67. sniffer.prot = malloc(sizeof(t_protocol *));
  68. /* 创建原始套接字,ETH_P_IP 表示侦听负载为 IP 数据报的以太网帧 */
  69. sd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP));
  70. if (sd < 0)
  71. {
  72. perror("socket(): ");
  73. return (EXIT_FAILURE);
  74. }
  75. getting_started();
  76. signal(SIGINT, &signal_white_now);/* 信号处理函数*/
  77. signal(SIGQUIT, &signal_white_now);
  78. /* 循环侦听以太网帧,并调用 ProcessPacket 函数解析 */
  79. while (1)
  80. {
  81. FD_ZERO(&fd_read);
  82. FD_SET(0, &fd_read);
  83. FD_SET(sd, &fd_read);
  84. /* 多路复用检测可读的套接字和标准输入 */
  85. res = select(sd + 1, &fd_read, NULL, NULL, NULL);
  86. if (res < 0)
  87. {
  88. close(sd);
  89. if (errno != EINTR)
  90. perror("select() ");
  91. return (EXIT_FAILURE);
  92. }
  93. else
  94. {
  95. /* 如果是标准输入可读,进入命令行处理程序 command_interpreter,暂时只支持 'quit' 命令 */
  96. if (FD_ISSET(0, &fd_read))
  97. {
  98. if (command_interpreter(sd) == 1)
  99. break;
  100. }
  101. /* 如果是套接字可读,则读取以太网数据帧的内容,并调用 ProcessPacket 函数解析出数据包的类型 */
  102. else if (FD_ISSET(sd, &fd_read))
  103. {
  104. saddr_size = sizeof(saddr);
  105. data_size = recvfrom(sd, buffer, 65536, 0, &saddr,(socklen_t*)&saddr_size); /* 读取以太网数据帧的内容 */
  106. if (data_size <= 0)
  107. {
  108. close(sd);
  109. perror("recvfrom(): ");
  110. return (EXIT_FAILURE);
  111. }
  112. ProcessPacket(buffer, data_size, &sniffer); /* 调用 ProcessPacket 函数解析出数据包的类型 */
  113. }
  114. }
  115. }
  116. close(sd);
  117. return (EXIT_SUCCESS);
  118. }
  119. void ProcessPacket(unsigned char* buffer, int size, t_sniffer *sniffer)
  120. {
  121. buffer = buffer + 6 + 6 + 2; /* 根据太网帧结构,前 6B 是目的 MAC 地址,接下来的是源 MAC 地址,接下来 2B 是帧长度,其余的是负载(上层的 IP 数据报) */
  122. struct iphdr *iph = (struct iphdr*)buffer;
  123. ++sniffer->prot->total; /* 数据包总数加 1 */
  124. /* 根据 TCP/IP 协议规定的 IP 数据报头部的 protocol 字段的值,判断上层的数据包类型 */
  125. switch (iph->protocol)
  126. {
  127. /* 1 表示 icmp 协议 */
  128. case 1:
  129. ++sniffer->prot->icmp;
  130. print_icmp_packet(buffer, size, sniffer);
  131. break;
  132. /* 2 表示 igmp 协议 */
  133. case 2:
  134. ++sniffer->prot->igmp;
  135. break;
  136. /* 6 表示 tcp 协议 */
  137. case 6:
  138. ++sniffer->prot->tcp;
  139. print_tcp_packet(buffer , size, sniffer);
  140. break;
  141. /* 17 表示 udp 协议 */
  142. case 17:
  143. ++sniffer->prot->udp;
  144. print_udp_packet(buffer , size, sniffer);
  145. break;
  146. default:
  147. ++sniffer->prot->others;
  148. break;
  149. }
  150. display_time_and_date(); /* 显示时间 */
  151. /* 打印 sniffer 中的信息 */
  152. printf("TCP : %d UDP : %d ICMP : %d IGMP : %d Others : %d Total : %d\n",
  153. sniffer->prot->tcp, sniffer->prot->udp,
  154. sniffer->prot->icmp, sniffer->prot->igmp,
  155. sniffer->prot->others, sniffer->prot->total);
  156. }

3.show_data.c

该文件就是往log.txt中写入数据包信息:

  1. #include <unistd.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <netinet/ip_icmp.h>
  6. #include <netinet/udp.h>
  7. #include <netinet/tcp.h>
  8. #include <netinet/ip.h>
  9. #include <sys/socket.h>
  10. #include <arpa/inet.h>
  11. #include "sniffer.h"
  12. #include "tools.h"
  13. /* 写 IP 头部到日志文件 */
  14. void print_ip_header(unsigned char *buf, int size, t_sniffer *sniffer)
  15. {
  16. unsigned short iphdrlen;
  17. struct iphdr *iph;
  18. struct sockaddr_in source;
  19. struct sockaddr_in dest;
  20. iph = (struct iphdr *)buf;
  21. iphdrlen = iph->ihl*4;
  22. (void)iphdrlen;
  23. (void)size;
  24. memset(&source, 0, sizeof(source));
  25. source.sin_addr.s_addr = iph->saddr;
  26. memset(&dest, 0, sizeof(dest));
  27. dest.sin_addr.s_addr = iph->daddr;
  28. fprintf(sniffer->logfile,"\n");
  29. fprintf(sniffer->logfile,"IP Header\n");
  30. fprintf(sniffer->logfile," |-IP Version : %d\n",(unsigned int)iph->version);
  31. fprintf(sniffer->logfile," |-IP Header Length : %d DWORDS or %d Bytes\n",(unsigned int)iph->ihl,((unsigned int)(iph->ihl))*4);
  32. fprintf(sniffer->logfile," |-Type Of Service : %d\n",(unsigned int)iph->tos);
  33. fprintf(sniffer->logfile," |-IP Total Length : %d Bytes(size of Packet)\n",ntohs(iph->tot_len));
  34. fprintf(sniffer->logfile," |-Identification : %d\n",ntohs(iph->id));
  35. fprintf(sniffer->logfile," |-TTL : %d\n",(unsigned int)iph->ttl);
  36. fprintf(sniffer->logfile," |-Protocol : %d\n",(unsigned int)iph->protocol);
  37. fprintf(sniffer->logfile," |-Checksum : %d\n",ntohs(iph->check));
  38. fprintf(sniffer->logfile," |-Source IP : %s\n",inet_ntoa(source.sin_addr));
  39. fprintf(sniffer->logfile," |-Destination IP : %s\n",inet_ntoa(dest.sin_addr));
  40. }
  41. /* 写 TCP 数据包到日志文件 */
  42. void print_tcp_packet(unsigned char *buf, int size, t_sniffer *sniffer)
  43. {
  44. unsigned short iphdrlen;
  45. struct iphdr *iph;
  46. struct tcphdr *tcph;
  47. iph = (struct iphdr *)buf;
  48. iphdrlen = iph->ihl * 4;
  49. tcph = (struct tcphdr*)(buf + iphdrlen);
  50. print_ip_header(buf, size, sniffer);
  51. /* 把 tcp 头信息写入日志文件中 */
  52. fprintf(sniffer->logfile,"\n");
  53. fprintf(sniffer->logfile,"TCP Header\n");
  54. fprintf(sniffer->logfile," |-Source Port : %u\n",ntohs(tcph->source));
  55. fprintf(sniffer->logfile," |-Destination Port : %u\n",ntohs(tcph->dest));
  56. fprintf(sniffer->logfile," |-Sequence Number : %u\n",ntohl(tcph->seq));
  57. fprintf(sniffer->logfile," |-Acknowledge Number : %u\n",ntohl(tcph->ack_seq));
  58. fprintf(sniffer->logfile," |-Header Length : %d DWORDS or %d BYTES\n" ,(unsigned int)tcph->doff,(unsigned int)tcph->doff*4);
  59. fprintf(sniffer->logfile," |-Urgent Flag : %d\n",(unsigned int)tcph->urg);
  60. fprintf(sniffer->logfile," |-Acknowledgement Flag : %d\n",(unsigned int)tcph->ack);
  61. fprintf(sniffer->logfile," |-Push Flag : %d\n",(unsigned int)tcph->psh);
  62. fprintf(sniffer->logfile," |-Reset Flag : %d\n",(unsigned int)tcph->rst);
  63. fprintf(sniffer->logfile," |-Synchronise Flag : %d\n",(unsigned int)tcph->syn);
  64. fprintf(sniffer->logfile," |-Finish Flag : %d\n",(unsigned int)tcph->fin);
  65. fprintf(sniffer->logfile," |-Window : %d\n",ntohs(tcph->window));
  66. fprintf(sniffer->logfile," |-Checksum : %d\n",ntohs(tcph->check));
  67. fprintf(sniffer->logfile," |-Urgent Pointer : %d\n",tcph->urg_ptr);
  68. fprintf(sniffer->logfile,"\n");
  69. fprintf(sniffer->logfile," DATA Dump ");
  70. fprintf(sniffer->logfile,"\n");
  71. fprintf(sniffer->logfile,"IP Header\n");
  72. PrintData(buf, iphdrlen, sniffer);
  73. fprintf(sniffer->logfile,"TCP Header\n");
  74. PrintData(buf+iphdrlen, tcph->doff*4, sniffer);
  75. fprintf(sniffer->logfile,"Data Payload\n");
  76. /* 把用户数据写入日志文件 */
  77. PrintData(buf + iphdrlen + tcph->doff*4,
  78. (size - tcph->doff*4-iph->ihl*4),
  79. sniffer );
  80. fprintf(sniffer->logfile,"\n###########################################################");
  81. }
  82. /* 写 UDP 数据包到日志文件 */
  83. void print_udp_packet(unsigned char *buf , int size, t_sniffer *sniffer)
  84. {
  85. unsigned short iphdrlen;
  86. struct iphdr *iph;
  87. struct udphdr *udph;
  88. iph = (struct iphdr *)buf;
  89. iphdrlen = iph->ihl*4;
  90. udph = (struct udphdr*)(buf + iphdrlen);
  91. fprintf(sniffer->logfile,"\n\n***********************UDP Packet*************************\n");
  92. print_ip_header(buf, size, sniffer);
  93. /* 把 udp 头信息写入日志文件中 */
  94. fprintf(sniffer->logfile,"\nUDP Header\n");
  95. fprintf(sniffer->logfile," |-Source Port : %d\n" , ntohs(udph->source));
  96. fprintf(sniffer->logfile," |-Destination Port : %d\n" , ntohs(udph->dest));
  97. fprintf(sniffer->logfile," |-UDP Length : %d\n" , ntohs(udph->len));
  98. fprintf(sniffer->logfile," |-UDP Checksum : %d\n" , ntohs(udph->check));
  99. fprintf(sniffer->logfile,"\n");
  100. fprintf(sniffer->logfile,"IP Header\n");
  101. PrintData(buf , iphdrlen, sniffer);
  102. fprintf(sniffer->logfile,"UDP Header\n");
  103. PrintData(buf+iphdrlen, sizeof(udph), sniffer);
  104. fprintf(sniffer->logfile,"Data Payload\n");
  105. /* 把用户数据写入日志文件 */
  106. PrintData(buf + iphdrlen + sizeof udph,
  107. (size - sizeof udph - iph->ihl * 4),
  108. sniffer);
  109. fprintf(sniffer->logfile,"\n###########################################################");
  110. }
  111. /* 写 ICMP 数据包到日志文件 */
  112. void print_icmp_packet(unsigned char *buf , int size, t_sniffer *sniffer)
  113. {
  114. unsigned short iphdrlen;
  115. struct iphdr *iph;
  116. struct icmphdr *icmph;
  117. iph = (struct iphdr *)buf;
  118. iphdrlen = iph->ihl * 4;
  119. icmph = (struct icmphdr *)(buf + iphdrlen);
  120. /* 把 icmp 头信息写入日志文件中 */
  121. fprintf(sniffer->logfile,"\n\n***********************ICMP Packet*************************\n");
  122. print_ip_header(buf , size, sniffer);
  123. fprintf(sniffer->logfile,"\n");
  124. fprintf(sniffer->logfile,"ICMP Header\n");
  125. fprintf(sniffer->logfile," |-Type : %d",(unsigned int)(icmph->type));
  126. if((unsigned int)(icmph->type) == 11)
  127. fprintf(sniffer->logfile," (TTL Expired)\n");
  128. else if((unsigned int)(icmph->type) == ICMP_ECHOREPLY)
  129. fprintf(sniffer->logfile," (ICMP Echo Reply)\n");
  130. fprintf(sniffer->logfile," |-Code : %d\n",(unsigned int)(icmph->code));
  131. fprintf(sniffer->logfile," |-Checksum : %d\n",ntohs(icmph->checksum));
  132. fprintf(sniffer->logfile,"\n");
  133. fprintf(sniffer->logfile,"IP Header\n");
  134. PrintData(buf, iphdrlen, sniffer);
  135. fprintf(sniffer->logfile,"UDP Header\n");
  136. PrintData(buf + iphdrlen , sizeof(icmph), sniffer);
  137. fprintf(sniffer->logfile,"Data Payload\n");
  138. /* 最后将用户数据写入日志文件中 */
  139. PrintData(buf + iphdrlen + sizeof(icmph),
  140. (size - sizeof(icmph) - iph->ihl * 4),
  141. sniffer);
  142. fprintf(sniffer->logfile,"\n###########################################################");
  143. }
  144. /* 写用户数据到日志文件 */
  145. void PrintData(unsigned char *buf, int size, t_sniffer *sniffer)
  146. {
  147. int i;
  148. for(i = 0 ; i < size ; i++)
  149. {
  150. if(i % 16 == 0)
  151. fprintf(sniffer->logfile, "\n");
  152. fprintf(sniffer->logfile, " %02X",(unsigned int)buf[i]);
  153. if( i == size - 1)
  154. fprintf(sniffer->logfile, "\n");
  155. }
  156. }

4.tools.h和tools.c定义了颜色、信号处理函数:

  1. #ifndef __COLOR_H__
  2. #define __COLOR_H__
  3. #include <stdio.h>
  4. #define CLEARSCREEN() printf("\033[H\033[2J")
  5. #define INITCOLOR(color) printf("\033[%sm", color)
  6. #define RED_COLOR "31"
  7. #define GREEN_COLOR "32"
  8. #define YELLOW_COLOR "33"
  9. #define BLUE_COLOR "34"
  10. #define ZERO_COLOR "0"
  11. #endif
  1. #include <signal.h>
  2. #include <stdio.h>
  3. void signal_white_now(int signum)
  4. {
  5. printf("Bye Bye !\n");
  6. }

三、实验截图

1.运行结果

2.log.txt中的一个UDP数据包

四、题外话

源码中有一个.sh文件,不知是什么?查资料后知道是shell脚本文件,shell脚本就是一些命令的集合。如果实现这样的操作:

  • 进入到/tmp/目录;
  • 列出当前目录中所有的文件名;
  • 把所有当前的文件拷贝到/root/目录下;
  • 删除当前目录下所有的文件。

简单的4步在shell窗口中需要敲4次命令,按4次回车。这样是不是很麻烦?当然这4步操作非常简单,如果是更加复杂的命令设置需要几十次操作呢?那样的话一次一次敲键盘会很麻烦。所以不妨把所有的操作都记录到一个文档中,然后去调用文档中的命令,这样一步操作就可以完成。这个文档就是shell脚本。shell脚本能很方便的去管理服务器,因为可以指定一个任务计划定时去执行某一个shell脚本实现需求。

2017-2018-1 20179202《Linux内核原理与分析》第十二周作业的更多相关文章

  1. 2018-2019-1 20189221 《Linux内核原理与分析》第八周作业

    2018-2019-1 20189221 <Linux内核原理与分析>第八周作业 实验七 编译链接过程 gcc –e –o hello.cpp hello.c / gcc -x cpp-o ...

  2. 2018-2019-1 20189221 《Linux内核原理与分析》第七周作业

    2018-2019-1 20189221 <Linux内核原理与分析>第七周作业 实验六 分析Linux内核创建一个新进程的过程 代码分析 task_struct: struct task ...

  3. 2018-2019-1 20189221 《Linux内核原理与分析》第六周作业

    2018-2019-1 20189221 <Linux内核原理与分析>第六周作业 实验五 实验过程 将Fork函数移植到Linux的MenuOS fork()函数通过系统调用创建一个与原来 ...

  4. 2018-2019-1 20189221《Linux内核原理与分析》第五周作业

    2018-2019-1 20189221<Linux内核原理与分析>第五周作业 实验四 实验过程 当用户态进程调用一个系统调用时,cpu切换到内核态并开始执行一个内核函数. 在Linux中 ...

  5. 2018-2019-1 20189221《Linux内核原理与分析》第三周作业

    2018-2019-1 20189221<Linux内核原理与分析>第三周作业 实验二 完成一个简单的时间片轮转多道程序内核代码 实验过程 在实验楼中编译内核 编写mymain.c函数和m ...

  6. 2019-2020-1 20199329《Linux内核原理与分析》第十三周作业

    <Linux内核原理与分析>第十三周作业 一.本周内容概述 通过重现缓冲区溢出攻击来理解漏洞 二.本周学习内容 1.实验简介 注意:实验中命令在 xfce 终端中输入,前面有 $ 的内容为 ...

  7. 2019-2020-1 20199329《Linux内核原理与分析》第十一周作业

    <Linux内核原理与分析>第十一周作业 一.本周内容概述: 学习linux安全防护方面的知识 完成实验楼上的<ShellShock 攻击实验> 二.本周学习内容: 1.学习& ...

  8. 2019-2020-1 20199329《Linux内核原理与分析》第八周作业

    <Linux内核原理与分析>第八周作业 一.本周内容概述: 理解编译链接的过程和ELF可执行文件格式 编程练习动态链接库的两种使用方式 使用gdb跟踪分析一个execve系统调用内核处理函 ...

  9. 2019-2020-1 20199329《Linux内核原理与分析》第七周作业

    <Linux内核原理与分析>第七周作业 一.本周内容概述: 对Linux系统如何创建一个新进程进行追踪 分析Linux内核创建一个新进程的过程 二.本周学习内容: 1.学习进程的描述 操作 ...

  10. 2019-2020-1 20199329《Linux内核原理与分析》第六周作业

    <Linux内核原理与分析>第六周作业 一.本周内容概述: 学习系统调用的相关理论知识,并使用库函数API和C代码中嵌入汇编代码两种方式使用getpid()系统调用 学习系统调用syste ...

随机推荐

  1. 机器学习算法整理(五)决策树_随机森林——鹃尾花实例 Python实现

    以下均为自己看视频做的笔记,自用,侵删! 还参考了:http://www.ai-start.com/ml2014/ In [8]: %matplotlib inline import pandas a ...

  2. Redis学习八:Redis的事务

    一.是什么 可以一次执行多个命令,本质是一组命令的集合.一个事务中的所有命令都会序列化,按顺序地串行化执行而不会被其它命令插入,不许加塞. 二.能干嘛 一个队列中,一次性.顺序性.排他性的执行一系列命 ...

  3. git push时报错fatal: Could not read from remote repository.

    后来发现,出现这个问题是因为仓库地址不对 使用如下命令先查看一下: $ git remote -v 发现跟github的地址不一致 然后在终端输入:git remote set-url origin ...

  4. 你知道吗?什么是 Responsive JavaScript ?

    Responsive Javascript 是什么? 简单来说就是可以根据浏览器的状态做出响应.响应包括对视窗大小的反应,根据你设备是否支持触摸事件或地理定位功能来决定是否显示特定内容,不一而足. 什 ...

  5. Go学习中

    教程 http://www.runoob.com/go/go-slice.html Go语言中的管道(Channel)总结 http://www.cnblogs.com/yetuweiba/p/436 ...

  6. BZOJ4816 数字表格

    4816: [Sdoi2017]数字表格 Time Limit: 50 Sec  Memory Limit: 128 MB Description Doris刚刚学习了fibonacci数列.用f[i ...

  7. BZOJ1822 Frozen Nova 冷冻波

    1822: [JSOI2010]Frozen Nova 冷冻波 Time Limit: 10 Sec  Memory Limit: 64 MB Description WJJ喜欢“魔兽争霸”这个游戏. ...

  8. 2019年湖南多校第一场||2018-2019 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2018)

    第一场多校就打的这么惨,只能说自己太菜了,还需继续努力啊- 题目链接: GYM链接:https://codeforces.com/gym/101933 CSU链接:http://acm.csu.edu ...

  9. POJ 1128 Frame Stacking (拓扑排序)

    题目链接 Description Consider the following 5 picture frames placed on an 9 x 8 array. ........ ........ ...

  10. 差分约束系统+输出路径(I - Advertisement POJ - 1752 )

    题目链接:https://cn.vjudge.net/contest/276233#problem/I 题目大意:输入k和n,然后输入n行,每一次输入两个数,代表开端和结尾,如果这个区间内点的个数大于 ...