Linux进程控制之exec函数族

本文最后更新于2016年5月25日,已超过 1 年没有更新,如果文章内容失效,还请反馈给我,谢谢!

=Start=

缘由:

工作、学习需要。

参考解答:

在进程的创建上Unix采用了一个独特的方法,它将进程创建与加载一个新进程映象分离。这样的好处是有更多的余地对两种操作进行管理。当我们创建了一个进程之后,通常将子进程替换成新的进程映象,这可以用exec系列的函数来进行。当然,exec系列的函数也可以将当前进程替换掉。例如:在shell命令行执行ps命令,实际上是shell进程调用fork复制一个新的子进程,再利用exec系统调用将新产生的子进程完全替换成ps进程。

因为exec系列函数并不创建任何新进程,所以前后的进程ID没有发生任何改变,exec所做的就是替换当前进程的正文段、数据、堆和栈段 exec系列函数包括:

这6个exec函数的参数很难记忆。函数名中的字符会给我们一些帮助:字母p表示该函数取filename作为参数,并且用PATH环境变量寻找可执行文件字母l表示该函数取一个参数表,它与字母v互斥;字母v表示该函数取一个argv[]矢量;最后,字母e表示该函数取envp[]数组,而不使用当前环境上面6个函数中只有execve是内核的系统调用,其它的5个只是库函数,最终都要调用该系统调用,它们之间的关系如下图:

UNIX_exec

以e结尾的两个函数(execle和execve)可以传递一个指向环境字符串指针数组的指针。其他四个函数则使用调用进程中的environ变量为新程序复制现有的环境。

exec 如果传入的是 filename,那么:

  • 如果包含 /,那么认为这是一个路径名 pathname;否则在 PATH 环境变量里查找到第一个可执行文件
  • 如果可执行文件不是链接器产生的,那么认为是一个 shell 文件,使用 /bin/sh 执行

执行 exec 函数,下面属性是不发生变化的:

  • 进程 ID 和父进程 ID
  • 实际用户 ID 和实际组 ID
  • 附加组 ID
  • 会话 ID
  • 控制终端
  • 闹钟余留时间
  • 当前工作目录
  • 根目录
  • umask
  • 文件锁
  • 进程信号屏蔽
  • 未处理信号
  • 资源限制
  • 进程时间

而下面属性是发生变化的:

  • 文件描述符如果存在 close-on-exec 标记,那么会关闭
  • 可执行程序存在设置用户 ID 和组 ID 位,那么有效用户 ID 和组 ID 会发生变化
参考链接:

=END=

声明: 除非注明,ixyzero.com文章均为原创,转载请以链接形式标明本文地址,谢谢!
https://ixyzero.com/blog/archives/2700.html

《Linux进程控制之exec函数族》上有17条评论

  1. linux系统调用和库函数调用的区别
    http://www.cnblogs.com/yanlingyin/archive/2012/04/23/2466141.html
    Linux系统调用列表
    http://www.ibm.com/developerworks/cn/linux/kernel/syscall/part1/appendix.html
    Linux系统调用和库函数调用
    http://blog.csdn.net/xifeijian/article/details/9081259
    系统调用和库函数有什么区别?
    https://www.zhihu.com/question/19930018

    所有的操作系统都提供多种服务的入口点,程序由此向内核请求服务。各种版本的UNIX实现都提供定义明确、数量有限、可直接进入内核的入口点,这些入口点被称为「系统调用」。
    ####
    系统调用通常用于底层文件访问(low-level file access),例如在驱动程序中对设备文件的直接访问。
    系统调用是操作系统相关的,因此一般没有跨操作系统的可移植性。
    系统调用发生在内核空间,因此如果在用户空间的一般应用程序中使用系统调用来进行文件操作,会有用户空间到内核空间切换的开销。事实上,即使在用户空间使用库函数来对文件进行操作,因为文件总是存在于存储介质上,因此不管是读写操作,都是对硬件(存储器)的操作,都必然会引起系统调用。也就是说,库函数对文件的操作实际上是通过系统调用来实现的。例如C库函数fwrite()就是通过write()系统调用来实现的。
    系统调用和普通库函数调用非常相似,只是系统调用由操作系统内核提供,运行于内核态,而普通的库函数调用由函数库或用户自己提供,运行于用户态。
    ##
    库函数调用是系统无关的,因此可移植性好。
    ##
    系统调用和库函数两者的关系——调用库函数是为了使用系统调用。 Linux几乎库函数和系统调用一一对应。Windows则不然。

  2. 如何在Linux下监控命令执行
    https://mp.weixin.qq.com/s/Qa3J6_dvdcSwoNF5W0fy1A

    Linux系统中执行命令主要依靠execve函数族,可以通过hook execve函数的方法监控命令执行,首先编写两个测试程序帮助寻找hook。

    确定了Hook点之后,就可以选择Hook方案了,这时有几个选择:

    在应用层:
    1、在ring3通过/etc/ld.so.preload劫持系统调用
    2、二次开发glibc加入监控代码(据说某产品就是这么做监控的)
    3、基于调试器思想通过ptrace()主动注入
    在应用层做Hook的好处是不受内核版本影响,通用性较好,而且技术难度相对较低,但是缺点更明显,因为ring3层的Hook都是针对glibc库做的监控,只要直接陷入0x80中断,就可以绕过glibc库直接调用系统调用。

    内核层的监控手段:
    1、API Inline Hook
    2、sys_call_table Hook
    3、IDT Hook
    4、利用LSM(Linux Security Module)

    API Inline Hook以及IDT Hook操作难度较大,而且兼容性较差,利用LSM监控API虽然性能最好,但是必须编译进内核才能使用,不可以实时安装卸载,而sys_call_table的Hook相对易于操作,作为防御者也可以直接从” /boot/System.map-uname -r ”中直接获取sys_call_table地址,也可以利用LKM(loadable kernel module)技术实现实时安装卸载,所以最后选择在内核层Hook sys_call_table实现监控。

  3. [译] Linux系统调用权威指南
    https://arthurchiao.github.io/blog/system-call-definitive-guide-zh/
    https://blog.packagecloud.io/eng/2016/04/05/the-definitive-guide-to-linux-system-calls/

    本文介绍了Linux程序是如何调用内核函数的。

    包括:
    几种不同的发起系统调用的方式
    如何自己写汇编代码发起系统调用(包括示例)
    系统调用的内核入口和内核出口
    glibc wrappers
    系统调用相关的内核bugs
    其他更多内容

发表评论

电子邮件地址不会被公开。 必填项已用*标注