概述
- 从____sys_sendmsg的实现上看,其在开辟内存后,拷贝了ctrl_buf以后,执行完send操作,就立马释放了,因此需要配合userfaultfd来使用,从而达到持久留驻在内存中的目的。
- 建议每次spray都使用单独的sock:
- 单个sock的可发送数据包的上限是0x5000
- 单次sendmsg可发送的数据包的上限也是0x5000
代码
```cdefine ALLOC_SIZE 0x1000
define PAGE_ROUND_DOWN(x) (((unsigned long)(x)) & (~(PAGE_SIZE-1)))
void spray_sendmsg(){ int sockfd = socket(AF_INET, SOCK_DGRAM, 0); struct msghdr msgh = {0}; void* ctrl_buf;
ctrl_buf = mmap( NULL, PAGE_ROUND_UP(ALLOC_SIZE), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0 );
memset( ctrl_buf, 0x61, PAGE_ROUND_DOWN(ALLOC_SIZE) );
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
addr.sin_family = AF_INET;
addr.sin_port = htons(6666);
// set user space buf(msg header)
msgh.msg_control = ctrl_buf;
msgh.msg_controllen = ALLOC_SIZE;
msgh.msg_name = (caddr_t)&addr;
msgh.msg_namelen = sizeof(addr);
sendmsg(sockfd, &msgh, 0);
}
<a name="UkQOI"></a>
### 内核实现
<a name="RnzoB"></a>
### 内核数据结构
```c
struct msghdr {
void *msg_name; /* ptr to socket address structure */
int msg_namelen; /* size of socket address structure */
struct iov_iter msg_iter; /* data */
/*
* Ancillary data. msg_control_user is the user buffer used for the
* recv* side when msg_control_is_user is set, msg_control is the kernel
* buffer used for all other cases.
*/
union {
void *msg_control;
void __user *msg_control_user; // 可控区域
};
bool msg_control_is_user : 1;
__kernel_size_t msg_controllen; // 可控区域长度
unsigned int msg_flags; /* flags on received message */
struct kiocb *msg_iocb; /* ptr to iocb for async requests */
};
代码分析
// 此刻msg_sys是保存用户态msghdr的内核变量,userd_address 和 allowed_msghdr_flags 是空
static int ____sys_sendmsg(struct socket *sock, struct msghdr *msg_sys,
unsigned int flags, struct used_address *used_address,
unsigned int allowed_msghdr_flags)
{
...
// 拷贝msg_control
flags |= (msg_sys->msg_flags & allowed_msghdr_flags);
ctl_len = msg_sys->msg_controllen;
if ((MSG_CMSG_COMPAT & flags) && ctl_len) {
err = cmsghdr_from_user_compat_to_kern(msg_sys, sock->sk, ctl,
sizeof(ctl));
if (err)
goto out;
ctl_buf = msg_sys->msg_control;
ctl_len = msg_sys->msg_controllen;
} else if (ctl_len) {
BUILD_BUG_ON(sizeof(struct cmsghdr) !=
CMSG_ALIGN(sizeof(struct cmsghdr)));
if (ctl_len > sizeof(ctl)) {
ctl_buf = sock_kmalloc(sock->sk, ctl_len, GFP_KERNEL); [1]
if (ctl_buf == NULL)
goto out;
}
err = -EFAULT;
if (copy_from_user(ctl_buf, msg_sys->msg_control_user, ctl_len)) [2]
goto out_freectl;
msg_sys->msg_control = ctl_buf;
msg_sys->msg_control_is_user = false;
}
...
err = sock_sendmsg(sock, msg_sys); [3]
...
out_freectl:
if (ctl_buf != ctl)
sock_kfree_s(sock->sk, ctl_buf, ctl_len); [4]
out:
return err;
}
[1] sock_kmalloc
在对用户态的size大小进行校验后,直接传递给了kmalloc来开辟内核空间
void *sock_kmalloc(struct sock *sk, int size, gfp_t priority)
{
if ((unsigned int)size <= sysctl_optmem_max &&
atomic_read(&sk->sk_omem_alloc) + size < sysctl_optmem_max) { [5]
void *mem;
atomic_add(size, &sk->sk_omem_alloc);
mem = kmalloc(size, priority);
if (mem)
return mem;
atomic_sub(size, &sk->sk_omem_alloc);
}
return NULL;
}
[2] copy_from_user
msg_sys是保存用户态msghdr的内核变量,其msg_control_user 及为用户态代码设置的buf,此刻将用户态spray数据拷贝到内核态空间中
[3] sock_sendmsg
调用具体协议注册的发包函数来进行实际的发包,eg.udp_sendmsg,进一步的内部实现不必关注
[4] sock_kfree_s
释放开辟的内核空间,意味着无法持久化的保留内存,因此该方法需要配合userfaultfd来实现spray内存的持久化
[5] sysctl_optmem_max
#define UIO_MAXIOV 1024
/* Maximal space eaten by iovec or ancillary data plus some space */
int sysctl_optmem_max __read_mostly = sizeof(unsigned long)*(2*UIO_MAXIOV+512);
EXPORT_SYMBOL(sysctl_optmem_max);
该全局变量限制了默认的单次sendmsg的上限是0x5000,且单个sock可发送的上限也是0x5000