系统调用execve的入口sys_execve()

/*
 * sys_execve() executes a new program.
 */
long sys_execve(const char __user *name,  //需要执行的文件的绝对路径(存于用户空间)
		const char __user *const __user *argv, //传入系统调用的参数(存于用户空间)		 
                const char __user *const __user *envp, struct pt_regs *regs) //regs是系统调用时系统堆栈的情况(详细解释请参看情景分析之系统调用)
{
	long error;
	char *filename;

	filename = getname(name); //copy *filename frome user space to system space.
	error = PTR_ERR(filename); 
	if (IS_ERR(filename))
		return error;
	error = do_execve(filename, argv, envp, regs);

#ifdef CONFIG_X86_32
	if (error == 0) {
		/* Make sure we don't return using sysenter.. */
                set_thread_flag(TIF_IRET);
        }
#endif

	putname(filename);
	return error;
}

我们首先关注标签__user,这个标签表示其后边的变量是指向用户空间的地址的(详细的解释,请参看深入Linux内核框架P27)。

关于sys_execve参数的说明:Not only the register set with the arguments and the name of the executable file (filename) but also pointers to the arguments and the environment of the program are passed as in system programming. The notation is slightly clumsy because argv and envp are arrays of pointers, and both the pointer tothe array itself as well as all pointers in the array are located in the userspace portion of the virtual address space. Recall from the Introduction that some precautions are required when userspace memoryis accessed from the kernel, and that the __user annotations allow automated tools to check if everything is handled properly.

接下来的getname将要执行的文件名从用户空间拷贝到系统空间会调用如下函数:

static char *getname_flags(const char __user * filename, int flags) 
{
	char *tmp, *result;

	result = ERR_PTR(-ENOMEM);
	tmp = __getname();  //allocate a physical page in system space as cache. Because the file's name could be very long. (hu xi ming, Page 306)
	if (tmp)  {
		int retval = do_getname(filename, tmp);

		result = tmp;
		if (retval < 0) {
			if (retval != -ENOENT || !(flags & LOOKUP_EMPTY)) {
				__putname(tmp);
				result = ERR_PTR(retval);
			}
		}
	}
	audit_getname(result);
	return result;
}
注意函数中的__getname();为文件名分配一个物理页面作为缓冲区,因为一个绝对路径可能很长,因此如果用临时变量的话,这个路径就被存储在系统堆栈段中,这显然是不合适的,因为系统堆栈段只有约7KB的空间。

之后调用do_getname()将filename从用户空间拷贝到分配到的系统物理页面上:

static int do_getname(const char __user *filename, char *page)
{
	int retval;
	unsigned long len = PATH_MAX;

	if (!segment_eq(get_fs(), KERNEL_DS)) {  //如果进程地址限制和KERNEL_DS不和相等,即当前进程没有运行在内核态
		if ((unsigned long) filename >= TASK_SIZE) //如果filname>=TASK_SIZE,则非法访问了
			return -EFAULT;
		if (TASK_SIZE - (unsigned long) filename < PATH_MAX)
			len = TASK_SIZE - (unsigned long) filename;    //这个是为什么????
	}

	retval = strncpy_from_user(page, filename, len);  //将filename从用户空间中拷贝到内核页面中。
	if (retval > 0) {
		if (retval < len)
			return 0;
		return -ENAMETOOLONG; 
	} else if (!retval)
		retval = -ENOENT;
	return retval;
}
对划红线部分代码的理解:在创建新进程的时候,有个copy_mm操作,将
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ROP(Return-oriented Programming)是一种攻击技术,它利用程序的已有代码(即gadget)来构造攻击。在构造syscall调用execve的ROP时,我们需要找到一些适合我们需要的gadget,以及一个能够满足我们需求的内存区域来存储我们的ROP链。 以下是一个构造syscall调用execve的ROP的示例: ``` ; pop rax ; ret gadget pop_rax_ret: 0x000000000040089c ; pop rdi ; ret gadget pop_rdi_ret: 0x000000000040089e ; pop rsi ; ret gadget pop_rsi_ret: 0x00000000004008a0 ; pop rdx ; ret gadget pop_rdx_ret: 0x00000000004008a2 ; syscall gadget syscall: 0x00000000004005f6 ; address of "/bin/sh" string bin_sh: db '/bin/sh',0 rop: ; set rax to 0x3b (execve syscall number) pop rax ; ret 0x000000000040089c 0x3b ; set rdi to the address of "/bin/sh" string pop rdi ; ret 0x000000000040089e bin_sh ; set rsi to 0 pop rsi ; ret 0x00000000004008a0 0x0 ; set rdx to 0 pop rdx ; ret 0x00000000004008a2 0x0 ; syscall syscall ``` 在这个例子中,我们使用了以下gadgets: - pop rax ; ret:弹出栈顶元素到rax寄存器中; - pop rdi ; ret:弹出栈顶元素到rdi寄存器中; - pop rsi ; ret:弹出栈顶元素到rsi寄存器中; - pop rdx ; ret:弹出栈顶元素到rdx寄存器中; - syscall:执行系统调用。 我们的ROP链的第一步是将rax寄存器设置为execve系统调用的编号(0x3b)。接下来,我们将/bin/sh字符串的地址传递给rdi寄存器。然后,我们将rsi和rdx寄存器都设置为0,因为execve系统调用不需要任何参数。最后,我们使用syscall gadget来执行系统调用。 请注意,我们需要在内存中准备/bin/sh字符串,因为execve系统调用需要它。在这个例子中,我们将/bin/sh字符串存储在名为bin_sh的标签中。在实际攻击中,这个字符串可以存储在任何我们可以访问的内存区域中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值