在学习Return-to-libc攻击方法时运用到了system函数,很好奇system具体是怎么实现的,所以在这里具体看一下:

基本知识

fork函数

fork函数通过系统调用创建一个与原来进程几乎完全相同的进程,一个进程调用fork函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。

  • 在父进程中,fork返回新创建子进程的进程ID
  • 在子进程中,fork返回0
  • 如果出现错误,fork返回一个负值

其实就相当于链表,进程形成了链表,父进程的fpid(p 意味point)指向子进程的进程id, 因为子进程没有子进程,所以其fpid为0.

这里对我们对fork的执行过程进行解释一下,来看它究竟做了哪些操作。 当你调用fork函数时,linux底层究竟怎样进行怎样的操作?为此,我查看linux内核0.11版本的源码来理解。

代码路径:init/main.c
static inline _syscall0(int,fork)

内核通过内联操作,在调用fork函数时,实际上是执行到unistd.h中的宏函数syscall0中去。对应代码:

#define __NR_setup	0	/* used only by init, to get system going */
#define __NR_exit	1
#define __NR_fork	2
#define __NR_read	3
#define __NR_write	4
#define __NR_open	5
		………
#define _syscall0(type,name) \
type name(void) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
	: "=a" (__res) \
	: "0" (__NR_##name)); \
if (__res >= 0) \
	return (type) __res; \
errno = -__res; \
return -1; \
} 首先进入_syscall0后,先执行:"0"(__NR_fork)是将fork在sys_call_table[]中对应的函数编号__NR_fork(也就是2)赋值给eax,(在sys_call_table[]中编号2即对应sys_fork函数)。然后执行int $0x80软中断。

system源码

#include
#include
#include
#include
int system(const char * cmdstring)
{
  pid_t pid;
  int status;
  if(cmdstring == NULL){
     
      return (1);
  }
  if((pid = fork())<0){

        status = -1;
  }
  else if(pid == 0){
    execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
    -exit(127); 
    }
  else{
        while(waitpid(pid, &status, 0) < 0){
          if(errno != EINTER){
            status = -1;
            break;
          }
        }
    }
    return status;
}

根据代码我们可以知道system函数的处理过程。当命令参数为NULL时返回1,如果不为NULL,则fork一个子进程,fork失败则返回-1。如果成功,就调用execl,路径是”/bin/sh”,一般这个会软链接到bash上,所以子进程就变成了一个shell进程。如果调用/bin/sh失败时返回127,成功调用后返回shell命令后的返回值但是此返回值也有可能为system()调用/bin/sh失败所返回的127,因此最好能再检查errno 来确认执行成功。
在调用system()期间SIGCHLD 信号会被暂时搁置,SIGINT和SIGQUIT 信号则会被忽略。

参考文档:http://blog.csdn.net/yankai0219/article/details/6730121