网络骇客初级之原始套接字(SOCK_RAW)

本文用实际程序完成了MAC数据包分析,网络数据分析,MAC地址扫描器和飞秋欺骗

在这里我把原来的入门改成了初级,因为对于原始套接字的操作确实在普通的TCP,UDP之上

TCP和UDP确实涵盖了普通的网络应用程序,但请注意“普通”二字,要成为一名骇客的你,可不能仅仅满足于写一些普通的网络小程序,而要直接对所有数据包进行分析,还要能够发送自己组装的数据包,踏入高级网络编程的领域,编写一些奇特的网络程序(嘿嘿!)。

注意所有程序都是在LINUX系统下实现的,当然在windows下不是不行,只是头文件什么的有点区别,留给感兴趣的同学去研究吧。

以接收为例,先来分析一个链路层的MAC数据包吧

还是老套路:

头文件

#include <stdio.h>

#include <netinet/in.h>

#include <sys/socket.h>

#include <netinet/ether.h>

#include <pthread.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <arpa/inet.h>

#include <fcntl.h>

1.创建原始套接字

int sock_raw_fd = socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL));

if(sock_raw_fd < 0)

{

perror("socket");

exit(-1);

}

2.定义接收缓冲区

unsigned char buf[1024]="";

3.储存源目的MAC地址缓冲区

unsigned char src_mac[18]="";

unsigned char dst_mac[18]="";

//从网卡直接接收数据包

int ret = recvfrom(sock_raw_fd,buf,sizeof(buf),0,NULL,NULL);

if(-1 != ret)

{

//打印数据包的源目的MAC

sprintf(dst_mac,"%02x:%02x:%02x:%02x:%02x:%02x",buf[0],buf[1],buf[2],buf[3],buf[4],buf[5]);

sprintf(src_mac,"%02x:%02x:%02x:%02x:%02x:%02x",buf[6],buf[7],buf[8],buf[9],buf[10],buf[11]);

printf("MAC:%s >> %s\n",src_mac,dst_mac);

printf("type == %02x%02x\n",buf[12],buf[13]);

}

close(sock_raw_fd);

因为在数据链路层传输的所有数据包前14个字节都是6字节目的MAC,6字节源MAC加上2字节的数据帧类型(如:IP:0800,ARP:0806,RARP:8035)

所以本程序取出接收缓冲区的前12个字节分别输出,显示源目的MAC地址

但我们能做的可不只是分析MAC地址而已,足以写一个被动网络嗅探器了

头文件

#include <stdio.h>

#include <stdlib.h>

#include <errno.h>

#include <unistd.h>

#include <sys/socket.h>

#include <sys/types.h>

#include <sys/ioctl.h>

#include <netinet/in.h>

#include <netinet/ip.h>

#include <netinet/if_ether.h>

#include <net/if_arp.h>

#include <net/if.h>

#include <net/ethernet.h>

#include <netpacket/packet.h>

#include <netinet/ether.h>

#include <string.h>

//子函数,用于显示源目的IP地址

void showip(unsigned char *buffer)

{

struct iphdr *iph;

char sipaddr[INET_ADDRSTRLEN] = "";

char dipaddr[INET_ADDRSTRLEN] = "";

iph = (struct iphdr*)(buffer+sizeof(struct ethhdr));

inet_ntop(AF_INET,&iph->saddr,sipaddr,INET_ADDRSTRLEN);

inet_ntop(AF_INET,&iph->daddr,dipaddr,INET_ADDRSTRLEN);

printf("IP: %s >> %s\n",sipaddr,dipaddr);

}

主函数

int main(int argc, char **argv)

{

int sock, n;

unsigned char buffer[1024];

struct ethhdr *eth;

struct iphdr *iph;

struct ifreq ethreq;

char type[5]="";

uint16_t *port=NULL;

//创建原始套接字

if(0>(sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))))

{

perror("socket");

exit(1);

}

//接收数据包

while(1)

{

bzero(buffer,sizeof(buffer));

n=recvfrom(sock,buffer,1024,0,NULL,NULL);

//分析数据包类型

sprintf(type,"%02x%02x",buffer[12],buffer[13]);

//0800IP数据包

if(0 == strcmp(type,"0800"))

{

printf("============IP===============\n");

//TCP还是UDP

if(0x06 == buffer[23])

{

printf("TCP\n");

}

else if(0x11 == buffer[23])

{

printf("UDP\n");

}

//端口

port= buffer+34;

printf("Port:%d >> ",ntohs(*port));

port= buffer+36;

printf("%d\n",ntohs(*port));

//输出IP地址

showip(buffer);

}

else if(0 == strcmp(type,"0806"))

{

printf("============ARP===============\n");

showip(buffer);

}

else if(0 == strcmp(type,"8035"))

{

printf("============RARP===============\n");

}

else

{

printf("============%s===============\n",type);

}

eth = (struct ethhdr*)buffer;

//显示MAC地址

printf("MAC: %02X:%02X:%02X:%02X:%02X:%02X >> %02X:%02X:%02X:%02X:%02X:%02X \n",eth->h_source[0],eth->h_source[1],eth->h_source[2],eth->h_source[3],eth->h_source[4],eth->h_source[5],eth->h_dest[0],eth->h_dest[1],eth->h_dest[2],eth->h_dest[3],eth->h_dest[4],eth->h_dest[5]);

}

}

在这里注意IP数据包的格式,一个IP数据包的前14个字节为MAC帧头部,接上一个20字节的IP报头,端口号就在数据包的第34个字节后面,不管是UDP数据包还是TCP数据包都一样,两个字节的源端口号和两个字节的目的端口号。

所以只要按照固定的格式分析接收到的数据包就可以了

被动的嗅探器只需要接收数据,我们还可以发送数据

试试弄一个MAC地址扫描器把

头文件

#include <stdio.h>

#include <stdlib.h>

#include <errno.h>

#include <unistd.h>

#include <fcntl.h>

#include <sys/socket.h>

#include <sys/types.h>

#include <sys/ioctl.h>

#include <netinet/in.h>

#include <netinet/ip.h>

#include <netinet/if_ether.h>

#include <net/if_arp.h>

#include <net/if.h>

#include <net/ethernet.h>

#include <netpacket/packet.h>

#include <netinet/ether.h>

#include <string.h>

#include <pthread.h>

//创建一个结构体定义套接字和文件描述符

typedef struct arg

{

int fd;

int sock_raw_fd;

}SOCK_FD;

//子线程接收数据包

void *recvarp(void *arg)

{

SOCK_FD sock_fds = *(SOCK_FD*)arg;

int sock_raw_fd = sock_fds.sock_raw_fd;

int fd = sock_fds.fd;

while(1)

{

unsigned char recv_buf[100]="";//接收缓冲区

recvfrom(sock_raw_fd,recv_buf,sizeof(recv_buf),0,NULL,NULL);

if(recv_buf[21] == 2)

{

//打印

unsigned char write_buf[20]="";

sprintf(write_buf,"%d.%d.%d.%d -- %02x:%02x:%02x:%02x:%02x:%02x\r\n",recv_buf[28],recv_buf[29],recv_buf[30],recv_buf[31],recv_buf[6],recv_buf[7],recv_buf[8],recv_buf[9],recv_buf[10],recv_buf[11]);

//写入文件

write(fd, write_buf, strlen(write_buf));

printf("%s",write_buf);

}

}

}

主函数

int main(int argc, char **argv)

{

SOCK_FD sock_fds;

int fd;

//创建一个文件用于保存接收的数据

fd = open("arplist.txt", O_CREAT|O_RDWR|O_APPEND|O_TRUNC, 0777);

//1创建套接字

int sock_raw_fd;

sock_raw_fd = socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ARP));

if(sock_raw_fd < 0)

{

perror("socket");

exit(-1);

}

//套接字和文件描述符赋值,作为参数传给线程

sock_fds.sock_raw_fd = sock_raw_fd;

sock_fds.fd = fd;

pthread_t pth;

pthread_create(&pth,NULL,recvarp,(void *)&sock_fds);

pthread_detach(pth);

//组装ARP请求包

unsigned char arp[42]={

//-------MAC头部---------14

0xff,0xff,0xff,0xff,0xff,0xff,//dst MAC广播包,全FF

0x00,0x0c,0x29,0xa8,0x4a,0xf0,//src MAC自己的MAC地址

0x08,0x06,//pro_type

//-------ARP包---------28

0x00,0x01,0x08,0x00,

0x06,0x04,0x00,0x01,

0x00,0x0c,0x29,0xa8,0x4a,0xf0,//src MAC

10,220,4,16,  //src IP

0x00,0x00,0x00,0x00,0x00,0x00,//DST MAC

// 10,220,4,17  //DST IP

0,0,0,0  //DST IP先写0,后面再赋值

};

//获取网卡接口地址

struct ifreq ethreq;

strncpy(ethreq.ifr_name, "eth0", IFNAMSIZ);

int ret = ioctl(sock_raw_fd, SIOCGIFINDEX, &ethreq);

if(ret == -1)

{

perror("ioctl");

close(sock_raw_fd);

exit(-1);

}

//sockaddr_ll 第三个成员赋值

struct sockaddr_ll sll;

bzero(&sll,sizeof(sll));

sll.sll_ifindex = ethreq.ifr_ifindex;

//目的IP赋值,从某一个网段开始

arp[38]=10;

arp[39]=220;

arp[40]=4;

arp[41]=0;

//sendto发送ARP请求

sendto(sock_raw_fd,arp,42,0,(struct sockaddr*)&sll,sizeof(sll));

//等待ARP应答

printf(" IP --  MAC\n");

while(1)//循环发送,每次IP地址加一

{

sendto(sock_raw_fd,arp,42,0,(struct sockaddr*)&sll,sizeof(sll));

arp[41]++;

if(arp[41] == 255)

{

break;

}

// printf("send IP:%d.%d.%d.%d\n",arp[38],arp[39],arp[40],arp[41]);

}

//关闭

close(sock_raw_fd);

close(fd);

return 0;

}

最后,我们再来写一个针对于飞秋软件的小程序吧,注意这里使用结构体对数据包进行赋值,所以要了解数据包的格式

头文件

#include<stdlib.h>

#include<stdio.h>

#include<errno.h>

#include<string.h>

#include<unistd.h>

#include<netdb.h>

#include<sys/socket.h>

#include<sys/types.h>

#include<netinet/in.h>

#include<netinet/ip.h>

#include<arpa/inet.h>

#include<linux/tcp.h>

#include<linux/udp.h>

#include <fcntl.h>

#include <sys/ioctl.h>

#include <netinet/if_ether.h>

#include <net/if_arp.h>

#include <net/if.h>

#include <net/ethernet.h>

#include <netpacket/packet.h>

#include <netinet/ether.h>

typedef unsigned char uchar;

//定义一个UDP伪头部结构体,用于UDP校验

struct falseudp

{

struct in_addr falseudp_src, falseudp_dst; /* source and dest address */

u_int8_t zero; /* 0 */

u_int8_t proto; /* 17 */

u_int16_t falseudp_len; /* udplen */

u_int16_t source; /* source port */

u_int16_t dest; /* dest port */

u_int16_t len; /* udp length */

u_int16_t check; /* udp checksum */

uchar msg[1024];

};

//计算校验和

unsigned short checksum(unsigned short *buf, int nword);

//主函数

int main(int argc, char**argv)

{

char username[20]="";

char hostname[20]="";

char msg_send[728]="";

uchar srcip[17]="";

uchar dstip[17]="";

printf("Fake IP:");

fflush(stdout);

fgets(srcip,sizeof(srcip)-1,stdin);

srcip[strlen(srcip)-1]='\0';

printf("Target IP:");

fflush(stdout);

fgets(dstip,sizeof(dstip)-1,stdin);

dstip[strlen(dstip)-1]='\0';

printf("Target username:");

fflush(stdout);

fgets(username,sizeof(username)-1,stdin);

username[strlen(username)-1]='\0';

printf("Target hostname:");

fflush(stdout);

fgets(hostname,sizeof(hostname)-1,stdin);

hostname[strlen(hostname)-1]='\0';

printf("send msg:");fflush(stdout);

fgets(msg_send,sizeof(msg_send)-1,stdin);

msg_send[strlen(msg_send)-1]='\0';

uchar srcMAC[6]={0xc8,0x9c,0xdc,0xb7,0x0f,0x19};

uchar dstMAC[6]={0x00,0x0c,0x29,0xa8,0x4a,0xf0};

unsigned short check_num;

//创建套接字

int sock_raw_fd;

sock_raw_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));

if(sock_raw_fd < 0)

{

perror("socket");

exit(-1);

}

//获取接口地址

struct ifreq ethreq;

strncpy(ethreq.ifr_name, "eth0", IFNAMSIZ);

int ret = ioctl(sock_raw_fd, SIOCGIFINDEX, &ethreq);

if(ret == -1)

{

perror("ioctl");

close(sock_raw_fd);

exit(-1);

}

//sockaddr_ll 第三个成员赋值

struct sockaddr_ll sll;

bzero(&sll,sizeof(sll));

sll.sll_ifindex = ethreq.ifr_ifindex;

//DATA,按照飞秋的消息格式组装数据包(可用wireshark分析出来)

uchar msg[1024]="";

int len = sprintf(msg, "%d:%d:%s:%s:%d:%s", 1, 123, username, hostname, 32, msg_send);

if(0 != (len%2))//还是计算校验和,数据长度必须是偶数,如果是奇数就在后面加一个0-‘\0’

{

len++;

msg[len]='\0';

}

printf("len:%d\n",len);

//组包,先写0,后面再用结构体赋值,mac+ip+udp

// uchar buf[2048]="";

uchar buf[2048]={

//MAC 14

0x00,0x00,0x00,0x00,0x00,0x00,//dst MAC

0x00,0x00,0x00,0x00,0x00,0x00,//src MAC

0x08,0x00,//proto_type

};

//源目的IP

memcpy(buf,srcMAC,6);

memcpy(buf+6,dstMAC,6);

//结构体

struct ip *ip;

struct udphdr *udp;

int ip_len,udp_len;

//IP包

ip_len = 20+8+len;

ip=(struct ip*)(buf+14);

ip->ip_v = IPVERSION;

ip->ip_hl = 5;

ip->ip_tos = 0;

ip->ip_len = htons(ip_len);//

ip->ip_id = 0;//0

ip->ip_off = 0;

ip->ip_ttl = MAXTTL;//MAXTTL

ip->ip_p = IPPROTO_UDP;

ip->ip_sum = 0;

inet_pton(AF_INET, srcip, &(ip->ip_src));

inet_pton(AF_INET, dstip, &(ip->ip_dst));

//计算校验和

check_num = checksum((unsigned short*)ip,10);

printf("ip-check_num:%x\n",check_num);//1ca4 and e9cb

ip->ip_sum = htons(check_num);

//udp包,端口2425

udp_len = 8+len;

udp = (struct udphdr*)(buf+14+20);

udp->source = htons(2425);

udp->dest =htons(2425);

udp->len = htons(udp_len);

udp->check = 0;

//DATA

memcpy(buf+42,msg,len);

// printf("%s\n",buf+42);

//////////

//UDP校验

struct falseudp falseudp;

struct falseudp *p=NULL;

//伪头部

inet_pton(AF_INET, srcip, &(falseudp.falseudp_src));

inet_pton(AF_INET, dstip, &(falseudp.falseudp_dst));

falseudp.zero = 0;

falseudp.proto = ip->ip_p;

falseudp.falseudp_len = udp->len;

falseudp.source = udp->source;

falseudp.dest = udp->dest;

falseudp.len = udp->len;

falseudp.check = 0;

memcpy(falseudp.msg,msg,len);

// printf("falseudp.msg:%s\n",falseudp.msg);

//计算UDP校验和

p=&falseudp;

check_num = checksum((unsigned short*)p,(20+len)/2);

udp->check = htons(check_num);//1ca4 and e9cb

printf("fudp-check_num:%x\n",check_num);

//////////

//发送数据直接用sendto()就可以了

int i=0;

while(1)

{

int ret_send = sendto(sock_raw_fd, buf, 42+len, 0, (struct sockaddr*)&sll,sizeof(sll));

// printf("ret_send:%d\n", ret_send);

i++;

if(i == 1)

{break;}

}

close(sock_raw_fd);

}

//=========================================================================

//函数:unsigned short checksum(unsigned short *buf, int nword)

//作用:用来进行TCP或者UDP的校验和的计算

//参数:

// buf:进行校验和的数据的起始地址

// nword:进行校验的数据的个数(注意本函数是2个Byte进行校验,所以nword应该为

// 实际数据个数的一半)

// UDP检验和的计算方法是:

// 1.按每16位求和得出一个32位的数;

// 2.如果这个32位的数,高16位不为0,则高16位加低16位再得到一个32位的数;

// 3.重复第2步直到高16位为0,将低16位取反,得到校验和。

//=========================================================================

unsigned short checksum(unsigned short *buf, int nword)

{

unsigned long sum;

for(sum = 0; nword > 0; nword--)

{

sum += htons(*buf);

buf++;

}

sum = (sum>>16) + (sum&0xffff);

sum += (sum>>16);

return ~sum;

}

原文地址:http://blog.51cto.com/13603157/2095503

时间: 2024-10-03 03:22:38

网络骇客初级之原始套接字(SOCK_RAW)的相关文章

原始套接字SOCK_RAW

原始套接字SOCK_RAW 实际上,我们常用的网络编程都是在应用层的报文的收发操作,也就是大多数程序员接触到的流式套接字(SOCK_STREAM)和数据包式套接字(SOCK_DGRAM).而这些数据包都是由系统提供的协议栈实现,用户只需要填充应用层报文即可,由系统完成底层报文头的填充并发送.然而在某些情况下需要执行更底层的操作,比如修改报文头.避开系统协议栈等.这个时候就需要使用其他的方式来实现. 一 原始套接字 原始套接字(SOCK_RAW)是一种不同于SOCK_STREAM.SOCK_DGR

原始套接字(SOCK_RAW)

本文转载:http://www.cnblogs.com/duzouzhe/archive/2009/06/19/1506699.html,在此感谢 原始套接字(SOCK_RAW). 应用原始套接字,我们可以编写出由TCP和UDP套接字不能够实现的功能. 注意原始套接字只能够由有 root权限的人创建. 10.1 原始套接字的创建 int sockfd(AF_INET,SOCK_RAW,protocol) 可以创建一个原始套接字.根据协议的类型不同我们可以创建不同类型的原始套接字 比如:IPPRO

LINUX 网络编程 原始套接字

一 原始套接字 原始套接字(SOCK_RAW)是一种不同于SOCK_STREAM.SOCK_DGRAM的套接字,它实现于系统核心.然而,原始套接字能做什么呢?首先来说,普通的套接字无法处理ICMP.IGMP等网络报文,而SOCK_RAW可以:其次,SOCK_RAW也可以处理特殊的IPv4报文:此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头.总体来说,SOCK_RAW可以处理普通的网络报文之外,还可以处理一些特殊协议报文以及操作IP层及其以上的数据. 既然SOCK_R

004.原始套接字,拼接UDP数据包,通信

大致流程: 建立一个client端,一个server端,自己构建IP头和UDP头,写入数据(hello,world!)后通过原始套接字(SOCK_RAW)将包发出去. server端收到数据后,打印UDP数据并发送确认消息(yes),client收到yes后将其打印. 其中: client端IP:192.168.11.104 端口:8600 server端IP:192.168.11.105 端口:8686 注意事项: 1.运行原始套接字socket需要有root权限. 2.注意主机字节序和网络字

原始套接字&amp;&amp;数据链路层访问

1. 原始套接字能力: (1) 进程可以读写ICMP,IGMP等分组,如ping程序: (2) 进程可以读写内核不处理协议字段的ipv4数据报:如OSPF等: (3) 进程可以使用IP_HDRINCL套接字选项自行构造ipv4首部: 2. 原始套接字的创建: int sockfd; sockfd = socket(AF_INET, SOCK_RAW, protocol); 开启ip头构造选项: const int on = 1; if (setsockopt(sockfd, IPPROTO_IP

网络骇客入门之TCP并发网页服务器

TCP并发服务器本来准备写在网络骇客入门之TCP编程后面的,但是因为代码有点长,所以就单独写了一篇 注意: 因为浏览器发送的数据比较多,所以作为本服务器的接受缓冲区recv_buf要大点,至少512字节,建议1024字节 如果接收不全的话,无论如何都不能将网页传给浏览器,这个bug卡了我一晚上,所以记得特别清楚. 创建线程时传给线程的参数注意写连接套接字的值,先转换为(void*)类型, pthread_create(&pth,NULL,msg_echo,(void *)connfd); 再在线

Linux网络编程——原始套接字实例:简单版网络数据分析器

通过<Linux网络编程--原始套接字编程>得知,我们可以通过原始套接字以及 recvfrom( ) 可以获取链路层的数据包,那我们接收的链路层数据包到底长什么样的呢? 链路层封包格式 MAC 头部(有线局域网) 注意:CRC.PAD 在组包时可以忽略 链路层数据包的其中一种情况: unsigned char msg[1024] = { //--------------组MAC--------14------ 0xb8, 0x88, 0xe3, 0xe1, 0x10, 0xe6, // dst

【转】网络编程原始套接字

转自:http://www.cnblogs.com/hnrainll/archive/2011/09/20/2182423.html SOCKET_STREAM 流式套接字      SOCKET_DGRAM        SOCKET_RAW 原始套接字    IPPROTO_IP IP协议    IPPROTO_ICMP INTERNET控制消息协议,配合原始套接字可以实现ping的功能    IPPROTO_IGMP INTERNET 网关服务协议,在多播中用到 在AF_INET地址族下,

《网络编程》原始套接字 ---ping程序实现

概述 基于字节流套接字(SOCK_STREAM)和数据报套接字(SOCK_DGRAM)不可以访问传输层协议,只是对应用层的报文进行操作,传输层的数据报格式都是由系统提供的协议栈实现,用户只需要填充相应的应用层报文,由系统完成底层报文首部的填充并发送.原始套接字(SOCK_RAW)可以访问位于基层的传输层协议,原始套接字没有端口号. 原始套接字(SOCK_RAW)是一种不同于 SOCK_STREAM.SOCK_DGRAM 的套接字,它实现于系统核心.原始套接字使进程可以读与写 ICMP.IGMP