C++新手,刚从学校出来实习,公司要求做一个分析pcap文件的小程序。
原理如下:
将每一个http的请求报文的host、URL、和其对应的tcp的源端口读取,写入list容器,接收到一个http响应报文,读取目的端口,与list容器里的源端口对比,对比成功则新建一个结构体将其对应的HOST和URL赋值给这个结构体,并删除list容器里的对应元素,假如这个响应报文携带数据,即状态码在200那一段的,以host为名字建立文件夹,并将数据写入这个文件夹,以URL命名。
现在的情况是 能建两个文件夹,也有文件写入但是不是按照预期写入,且有corrupted double-linked list和aborted (core dumped)报错。
各位大哥看看是什么原因,找了两天也不知道该怎么改。谢谢了!
前两个是请求报文和响应报文的数据写入,算测试。
红色框出来的是创建的文件夹
下面贴代码:
main.cpp
原理如下:
将每一个http的请求报文的host、URL、和其对应的tcp的源端口读取,写入list容器,接收到一个http响应报文,读取目的端口,与list容器里的源端口对比,对比成功则新建一个结构体将其对应的HOST和URL赋值给这个结构体,并删除list容器里的对应元素,假如这个响应报文携带数据,即状态码在200那一段的,以host为名字建立文件夹,并将数据写入这个文件夹,以URL命名。
现在的情况是 能建两个文件夹,也有文件写入但是不是按照预期写入,且有corrupted double-linked list和aborted (core dumped)报错。
各位大哥看看是什么原因,找了两天也不知道该怎么改。谢谢了!
前两个是请求报文和响应报文的数据写入,算测试。
红色框出来的是创建的文件夹
下面贴代码:
main.cpp
#include "pcap.h" #include "method.hpp" #include <iostream> #include <memory.h> #include <string> #include <vector> #include <list> #include <cstdio> #include <fstream> #include <iomanip> #include <sys/stat.h> #include <sys/types.h> #include <dirent.h> #include <stdio.h> using namespace std; int main(int argc, char *argv[]){ int a=1 ,b = 1,i=1; pcap_header pcap_head_buf; pkt_header pkt_head_buf; ip_header ip_head_buf; tcp_header tcp_head_buf; u_int16 srcport_buf; u_int16 dstport_buf; FILE *fp = NULL; if (fopen("/home/yiyada/http/http7.pcap", "rb") == NULL){ printf("Fail to open the file"); }else fp = fopen("/home/yiyada/http/http7.pcap", "rb"); getPcapFileHead(fp, pcap_head_buf); fseek(fp, 0, SEEK_END);//改变流指针位置,失败不改变 long fileSize = ftell(fp); long fpOffset = sizeof(pcap_header); fseek(fp, fpOffset, SEEK_SET);//回调指针 /*!!*/ list <HttpList1> httplist; /*!!*/ while ((fseek(fp, fpOffset, SEEK_SET) == 0) && (fpOffset < fileSize)){//循环处理每一个Pkt_Header getPktHead(fp, pkt_head_buf); fpOffset += (sizeof(pkt_header)+pkt_head_buf.caplen); //fpOffset 当前位置 +sizeof( pkt_header) +sizeof (pkt_data) ,得到下一网络帧的 offset u_int16 framType = getFramType(fp, pcap_head_buf.linktype); if (framType == 0xdd86){//IPV6链接 , 跳过该网络帧 continue; } else{ getIpHead(fp, ip_head_buf); if (ip_head_buf.Protocol != 0x06){// Protocol != 0x06 表示非TCP链接 , 跳过该网络帧 continue; } else { //TCP 链接类型 getTcpHead(fp, tcp_head_buf); fseek(fp, -20, SEEK_CUR); fread(&srcport_buf, 2, 1, fp); fread(&dstport_buf, 2, 1, fp); fseek(fp, 16, SEEK_CUR); long tcp_data_size = fpOffset - ftell(fp); // 当前位置在一个 tcp_header 后 ,fpOffset - 当前位置 得到 tcp_data 的长度 if(tcp_data_size != 0) { u_int8 tempBuf[4]; u_int8 tempBuf2[12]; string methodBuf;//似乎没有用 string urlBuf; string hostBuf; string uaBuf; fread(tempBuf, 4, 1, fp); fseek(fp, -4, SEEK_CUR); char tcp_data_buf[100000];//总的传输字节数应该小于100000 memset(tcp_data_buf, 0, sizeof(tcp_data_buf) / sizeof(char));//内存清空 if((tempBuf[0] == 0x50 && tempBuf[1] == 0x4f && tempBuf[2] == 0x53 && tempBuf[3] == 0x54) || (tempBuf[0] == 0x47 && tempBuf[1] == 0x45 && tempBuf[2] == 0x54) )//两个条件分别表示"POST"和"GET",判断成功表明该网络帧包含了一个http,get或post链接 { fread(tcp_data_buf, tcp_data_size, 1, fp); matchHttp(tcp_data_buf, methodBuf, urlBuf, hostBuf, uaBuf); /*!!*/ HttpList1 node = { urlBuf,hostBuf,srcport_buf }; httplist.push_back(node);//将请求报文加入list /*!!!!!!!!!!!*/ //写请求报文 ofstream f1("/home/yiyada/http/request_msg.txt", ios::app); //打开文件用于写,若文件不存在就创建它 if (!f1)return 0; //打开文件失败则结束运行 f1 << "No." << a << endl; f1 << "URL:"<<hostBuf+urlBuf<< endl; f1 << "user-agent:" << uaBuf << endl; f1 << "===========+===========+===========+===========" << endl; f1.close(); a++; } else//写响应报文 { fread(tempBuf2, 12, 1, fp); fseek(fp, -12, SEEK_CUR); if (tempBuf2[0] == 0x48 && tempBuf2[1] == 0x54 && tempBuf2[2] == 0x54 && tempBuf2[3] == 0x50 && tempBuf2[4] == 0x2f && tempBuf2[5] == 0x31 && tempBuf2[6] == 0x2e && tempBuf2[7] == 0x31 && tempBuf2[8] == 0x20 && tempBuf2[9] == 0x32 && tempBuf2[10] == 0x30 && ( 0<= tempBuf2[11] <=9 )){//HTTP/1.1 202判断 fread(tcp_data_buf, tcp_data_size, 1, fp); char *data; if (strstr(tcp_data_buf, "\r\n\r\n") == NULL) cout << "error" << endl; else { data = strstr(tcp_data_buf, "\r\n\r\n"); } HttpList2 index_http=analyse(httplist,srcport_buf);//找到对应响应报文并删除 if(opendir(index_http.host.c_str())==NULL){ mkdir(index_http.host.c_str(),S_IRWXU|S_IRWXG|S_IRWXO); } ofstream out("/home/yiyada/http/res_msg.txt", ios::app);//打开文件用于写,若文件不存在就创建它 if (!out)return 0; //打开文件失败则结束运行 out << (data+4) << endl; out.close(); rename("/home/yiyada/http/res_msg",index_http.URL.c_str()); ofstream f1("/home/yiyada/http/response_msg.txt", ios::app);//打开文件用于写,若文件不存在就创建它 if (!f1)return 0; //打开文件失败则结束运行 f1 << "No." << b << endl; f1 << (data+4) << endl; f1 << "===========+===========+===========+===========" << endl; f1.close(); b++; } } } } } } return 0; }
method.hpp
#ifndef METHOD_H #define METHOD_H #include "pcap.h" #include <cstdio> #include <iostream> #include <vector> #include <string> #include <list> using namespace std; typedef struct HttpList1 { string URL; string host; u_int16 port; }HttpList1; typedef struct HttpList2 { string URL; string host; }HttpList2; void getPcapFileHead(FILE *fp, pcap_header &pcap_head){ fread(&pcap_head, sizeof(pcap_header), 1, fp);//返回真实写入的项数 } void getPktHead(FILE *fp, pkt_header &pkt_head){ fread(&pkt_head, sizeof(pkt_header), 1, fp); } u_int32 getFramType(FILE *fp, u_int32 linktype){ if (linktype == 0x71){ Linux_cooked_capture temp; fread(&temp, sizeof(temp), 1, fp); return temp.FrameType; } else if (linktype == 0x01){ Ethernet temp; fread(&temp, sizeof(temp), 1, fp); return temp.FrameType; } else return linktype; } void getIpHead(FILE *fp, ip_header &ip_head_buf){ fread(&ip_head_buf, sizeof(ip_header), 1, fp); } void getTcpHead(FILE *fp, tcp_header &tcp_head_buf){ fread(&tcp_head_buf, sizeof(tcp_header), 1, fp); } //请求报文 void matchHttp(char tcp_data_buf[], string&methodBuf, string&urlBuf, string&hostBuf, string&uaBuf){ vector <string> tempStrVector;//声明容器 string tempSring(tcp_data_buf); for (string::size_type beginPos = 0; beginPos != tempSring.size();){ string::size_type endPos = beginPos; while (++endPos && endPos != tempSring.size()){ if (tempSring[endPos] == "\n"){ break; } } tempStrVector.push_back(tempSring.substr(beginPos, endPos - beginPos));//一行一行的存入 if (endPos == tempSring.size()){ break; } beginPos = endPos; }//把数据存入VECTOR for (vector <string>::iterator posVector = tempStrVector.begin(); posVector != tempStrVector.end(); ++posVector){ if (string::size_type tempPos = (*posVector).find("GET") != (*posVector).npos){ methodBuf = "GET"; string::size_type endPos = (*posVector).find("HTTP/1.1"); urlBuf = (*posVector).substr(tempPos + sizeof("GET") - 1, endPos - tempPos - sizeof("GET")); } // “ GET ” 和 “ HTTP/1.1” 之间字符串为 url if (string::size_type tempPos = (*posVector).find("POST") != (*posVector).npos){ string::size_type endPos = (*posVector).find("HTTP/1.1"); methodBuf = "POST"; urlBuf = (*posVector).substr(tempPos + sizeof("POST") - 1, endPos - tempPos - sizeof("POST")); } // “ POST ” 和 “ HTTP/1.1” 之间的字符串为 url if (string::size_type tempPos = (*posVector).find("Host:") != (*posVector).npos){ hostBuf = (*posVector).substr(tempPos + sizeof("Host:")); } //" Host:" 后的字符串为 host if (string::size_type tempPos = (*posVector).find("User-Agent:") != (*posVector).npos){ uaBuf = (*posVector).substr(tempPos + sizeof("User-Agent:")); } // " User-Agent:" 后的字符串为 ua } } //响应报文 HttpList2 analyse(list<HttpList1>& httplist,u_int16 srcport_buf){ list < HttpList1 > ::iterator lis = httplist.begin(); for (lis = httplist.begin(); lis != httplist.end(); ++lis){ if ((*lis).port==srcport_buf) { HttpList2 found_req = {(*lis).URL,(*lis).host}; lis = httplist.erase(lis); return found_req; } } } #endif
pcap.h
#ifndef DEFINEPCAP_H #define DEFINEPCAP_H typedef unsigned int u_int32; typedef unsigned short u_int16; typedef unsigned char u_int8; typedef int int32; typedef struct pcap_header{ u_int32 magic; u_int16 Ver_major; u_int16 Ver_minor; int32 thiszone; u_int32 sigfigs; u_int32 snaplen; u_int32 linktype; }pcap_header;//占24字节 typedef struct timestamp{ u_int32 timestamp_s; u_int32 timestamp_ms; }timestamp;//时间戳 typedef struct pkt_header{ timestamp ts; u_int32 caplen; u_int32 len; }pkt_header; typedef struct Ethernet{ u_int8 DstMAC[6];//目的MAC地址 u_int8 SrcMAC[6];//源MAC地址 u_int16 FrameType;//以太网帧类型 }Ethernet;//4字节 typedef struct Linux_cooked_capture{ u_int16 package_type; u_int16 address_type; u_int16 address_length; u_int16 un_used[4]; u_int16 FrameType; //帧类型 }Linux_cooked_capture;//10字节 typedef struct ip_header{ u_int8 Ver_HLen; u_int8 TOS; u_int16 TotalLen; u_int16 ID; u_int16 Flag_Segment; u_int8 TTL; u_int8 Protocol; u_int16 Checksum; u_int32 SrcIP; u_int32 DstIP; }ip_header;//20字节 typedef struct tcp_header{ u_int16 SrcPort; u_int16 DstPort; u_int32 SeqNo; u_int32 AckNo; u_int8 HeaderLen;//数据报头的长度(4 bit) + 保留(4 bit) u_int8 Flags; u_int16 Window;//窗口大小 u_int16 Checksum;//校验和 u_int16 UrgentPointer;//紧急指针 }tcp_header;//20字节 #endif
解决方案
100
本人试了下,这个程序很多隐患,一有逻辑错误segmentation fault,生成core dump。
先打开系统生成core:
ulimit -c unlimited
带上-g参数
g++ main.cpp -g
崩溃会产生core文件,运行:
gdb a.out core
进入gdb,输入backtrace可以看到堆栈信息,基本上就是那一行出问题,肯定是空指针,去做响应的修改,一直修改直到程序正常。
若要看汇编,直接敲入 layout asm可以查看汇编。
刚开始学者更应该学习调试技巧,不是一句不熟就可以掩盖过去,这都得靠本人。
先打开系统生成core:
ulimit -c unlimited
带上-g参数
g++ main.cpp -g
崩溃会产生core文件,运行:
gdb a.out core
进入gdb,输入backtrace可以看到堆栈信息,基本上就是那一行出问题,肯定是空指针,去做响应的修改,一直修改直到程序正常。
若要看汇编,直接敲入 layout asm可以查看汇编。
刚开始学者更应该学习调试技巧,不是一句不熟就可以掩盖过去,这都得靠本人。