续上一篇<<ELF:加载过程>>中分析elf解析器、解析器填充等内容后,本章分析elf可执行程序加载过程。

目录

1. 源码流程

1.1 execve

2. 源码结构

3. 部分结构定义

4. 扩展函数

内容

1. 源码流程

1.1 execve

execve函数用于装载一个可执行文件以进程为单位加载到内存中,execve在内核空间中的调用可以追溯到kernel_execve -> run_init_process函数,而用户空间通过SYSCALL_DEFINE3(execve…)->do_execve进入函数:

static int do_execve(struct filename *filename,const char __user *const __user *__argv,const char __user *const __user *__envp)
{struct user_arg_ptr argv = { .ptr.native = __argv };struct user_arg_ptr envp = { .ptr.native = __envp };return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);// #define AT_FDCWD   -100   用于指示openat应使用当前工作目录(./)
}
||
\/
static int do_execveat_common(int fd, struct filename *filename,struct user_arg_ptr argv,struct user_arg_ptr envp,int flags)
{struct linux_binprm *bprm;int retval;...if ((current->flags & PF_NPROC_EXCEEDED) &&is_ucounts_overlimit(current_ucounts(), UCOUNT_RLIMIT_NPROC, rlimit(RLIMIT_NPROC))) {// 如果已经超过用户最大进程数量 并且 用户的某个(计数中的)命名空间大于最大值,返回错误// #define PF_NPROC_EXCEEDED       0x00001000  超出最大进程数retval = -EAGAIN;goto out_ret;}current->flags &= ~PF_NPROC_EXCEEDED; // 去掉用户进程数量状态标志bprm = alloc_bprm(fd, filename);  // 分配bprm// 用于保存可执行文件相关信息// 可执行文件路径,执行参数,环境变量等等retval = count(argv, MAX_ARG_STRINGS);// 检查参数是否有效,参数数量是否大于最大值,并捕获异常信号、KILL信号等等// #define MAX_ARG_STRINGS 0x7FFFFFFFif (retval == 0)pr_warn_once("process '%s' launched '%s' with NULL argv: empty string added\n",current->comm, bprm->filename);bprm->argc = retval;retval = count(envp, MAX_ARG_STRINGS); // 环境变量检查retval = bprm_stack_limits(bprm); // 检查栈是否超出限制/* 从内核复制和参数/环境字符串到进程堆栈 */retval = copy_string_kernel(bprm->filename, bprm);bprm->exec = bprm->p;retval = copy_strings(bprm->envc, envp, bprm);retval = copy_strings(bprm->argc, argv, bprm);/* 当 argv 为空时,添加一个空字符串 ("") 作为 argv[0] 以确保从 argv[1] 开始处理的混淆用户空间程序不会最终遍历 envp */if (bprm->argc == 0) {retval = copy_string_kernel("", bprm);if (retval < 0)goto out_free;bprm->argc = 1;}retval = bprm_execve(bprm, fd, filename, flags); // 可执行文件解析后执行
out_free:free_bprm(bprm);out_ret:putname(filename);return retval;
}

bprm_execve分析

bprm_execve

static int bprm_execve(struct linux_binprm *bprm,int fd, struct filename *filename, int flags)
{struct file *file;int retval;retval = prepare_bprm_creds(bprm); // bprm->cred 获取task_struct结构对象(current)的cred结构// cred结构(credentials)用于进程安全上下文相关/* 确定执行调用者必须持有的提议程序的安全性 ->cred_guard_mutex以防止 PTRACE_ATTACH 或 seccomp 线程同步 */check_unsafe_exec(bprm);current->in_execve = 1;file = do_open_execat(fd, filename, flags); // 打开可执行文件sched_exec(); // 找到最小负载的CPU,执行可执行文件(线程)bprm->file = file;/* 记录从 O_CLOEXEC fd 派生的名称在执行后将不可访问这允许 exec 中的代码在可执行文件未映射到解释器并且打开的文件描述符未传递给解释器时选择失败与让解释器启动然后在发现可执行文件不可访问时立即失败相比,这提供了更好的用户体验 */if (bprm->fdpath && get_close_on_exec(fd))bprm->interp_flags |= BINPRM_FLAGS_PATH_INACCESSIBLE; // 执行后二进制文件名将无法访问// #define BINPRM_FLAGS_PATH_INACCESSIBLE_BIT 2 // #define BINPRM_FLAGS_PATH_INACCESSIBLE (1 << BINPRM_FLAGS_PATH_INACCESSIBLE_BIT)/* 设置 bprm->cred 不变的部分 */retval = security_bprm_creds_for_exec(bprm);retval = exec_binprm(bprm); // 二进制文件解析image,填充审计结构, trace跟踪打印,事件设置等等

exec_binprm分析,继续看bprm_execve函数:

     /* execve succeeded */current->fs->in_exec = 0;current->in_execve = 0;rseq_execve(current); // rseq赋空值acct_update_integrals(current);task_numa_free(current, false);return retval;
...
}

2. 源码结构

3. 部分结构定义

cred 任务的安全上下文

struct cred {atomic_t    usage;
#ifdef CONFIG_DEBUG_CREDENTIALSatomic_t subscribers;    /* number of processes subscribed */void        *put_addr;unsigned  magic;
#define CRED_MAGIC  0x43736564
#define CRED_MAGIC_DEAD 0x44656144
#endifkuid_t        uid;        /* real UID of the task */kgid_t        gid;        /* real GID of the task */kuid_t        suid;       /* saved UID of the task */kgid_t       sgid;       /* saved GID of the task */kuid_t       euid;       /* effective UID of the task */kgid_t       egid;       /* effective GID of the task */kuid_t       fsuid;      /* UID for VFS ops */kgid_t     fsgid;      /* GID for VFS ops */unsigned   securebits; /* SUID-less security management */kernel_cap_t cap_inheritable; /* caps our children can inherit */kernel_cap_t    cap_permitted;  /* caps we're permitted */kernel_cap_t  cap_effective;  /* caps we can actually use */kernel_cap_t  cap_bset;   /* capability bounding set */kernel_cap_t   cap_ambient;    /* Ambient capability set */
#ifdef CONFIG_KEYSunsigned char jit_keyring;    /* default keyring to attach requested* keys to */struct key    *session_keyring; /* keyring inherited over fork */struct key   *process_keyring; /* keyring private to this process */struct key   *thread_keyring; /* keyring private to this thread */struct key *request_key_auth; /* assumed request_key authority */
#endif
#ifdef CONFIG_SECURITYvoid      *security;  /* LSM security */
#endifstruct user_struct *user; /* real user ID subscription */struct user_namespace *user_ns; /* user_ns the caps and keyrings are relative to. */struct ucounts *ucounts;struct group_info *group_info;   /* supplementary groups for euid/fsgid *//* RCU deletion */union {int non_rcu;          /* Can we skip RCU deletion? */struct rcu_head  rcu;        /* RCU deletion hook */};
} __randomize_layout;

nameidata 保存文件相关的信息,这是一个临时结构,仅仅用在寻找目标节点(或创建临时文件)的过程

struct nameidata {struct path    path;struct qstr    last;struct path    root;struct inode   *inode; /* path.dentry.d_inode */unsigned int   flags, state;unsigned   seq, m_seq, r_seq;int       last_type;unsigned  depth;int       total_link_count;struct saved {struct path link;struct delayed_call done;const char *name;unsigned seq;} *stack, internal[EMBEDDED_LEVELS];struct filename  *name;struct nameidata *saved;unsigned  root_seq;int        dfd;kuid_t      dir_uid;umode_t     dir_mode;
} __randomize_layout;

migration_arg 线程迁移到目标cpu

struct migration_arg {struct task_struct     *task;int               dest_cpu;struct set_affinity_pending    *pending;
};

audit_context per-task的审计上下文

struct audit_context {int            dummy;  /* must be the first element */enum {AUDIT_CTX_UNUSED,  /* audit_context is currently unused */AUDIT_CTX_SYSCALL,   /* in use by syscall */AUDIT_CTX_URING, /* in use by io_uring */} context;enum audit_state    state, current_state;unsigned int     serial;     /* serial number for record */int           major;      /* syscall number */int         uring_op;   /* uring operation */struct timespec64   ctime;      /* time of syscall entry */unsigned long       argv[4];    /* syscall arguments */long         return_code;/* syscall return code */u64            prio;int            return_valid; /* return code is valid *//** The names_list is the list of all audit_names collected during this* syscall.  The first AUDIT_NAMES entries in the names_list will* actually be from the preallocated_names array for performance* reasons.  Except during allocation they should never be referenced* through the preallocated_names array and should only be found/used* by running the names_list.*/struct audit_names  preallocated_names[AUDIT_NAMES];int         name_count; /* total records in names_list */struct list_head    names_list;    /* struct audit_names->list anchor */char            *filterkey; /* key for rule that triggered record */struct path     pwd;struct audit_aux_data *aux;struct audit_aux_data *aux_pids;struct sockaddr_storage *sockaddr;size_t sockaddr_len;/* Save things to print about task_struct */pid_t          pid, ppid;kuid_t            uid, euid, suid, fsuid;kgid_t           gid, egid, sgid, fsgid;unsigned long        personality;int         arch;pid_t          target_pid;kuid_t           target_auid;kuid_t          target_uid;unsigned int     target_sessionid;u32            target_sid;char         target_comm[TASK_COMM_LEN];struct audit_tree_refs *trees, *first_trees;struct list_head killed_trees;int tree_count;int type;union {struct {int nargs;long args[6];} socketcall;struct {kuid_t          uid;kgid_t          gid;umode_t         mode;u32            osid;int            has_perm;uid_t          perm_uid;gid_t          perm_gid;umode_t            perm_mode;unsigned long     qbytes;} ipc;struct {mqd_t          mqdes;struct mq_attr        mqstat;} mq_getsetattr;struct {mqd_t            mqdes;int           sigev_signo;} mq_notify;struct {mqd_t           mqdes;size_t            msg_len;unsigned int        msg_prio;struct timespec64  abs_timeout;} mq_sendrecv;struct {int           oflag;umode_t           mode;struct mq_attr     attr;} mq_open;struct {pid_t            pid;struct audit_cap_data   cap;} capset;struct {int            fd;int          flags;} mmap;struct open_how openat2;struct {int            argc;} execve;struct {char          *name;} module;struct {struct audit_ntp_data    ntp_data;struct timespec64  tk_injoffset;} time;};int fds[2];struct audit_proctitle proctitle;
};

rseq 可重新启动的序列

/* struct rseq 在 4 * 8 字节上对齐,以确保它始终包含在单个缓存行中,允许每个线程有一个 struct rseq,rseq的主要使用场景之一是获取执行当前线程的CPU编号,通常也就是指向per-cpu数据结构的索引值 */struct rseq {__u32 cpu_id_start;__u32 cpu_id;__u64 rseq_cs;__u32 flags;
} __attribute__((aligned(4 * sizeof(__u64))));

4. 扩展函数

exec_binprm

二进制文件解析image,填充审计结构, trace跟踪打印,事件设置等等

static int exec_binprm(struct linux_binprm *bprm)
{.../* 这允许在严重失败之前进行 4 级 binfmt 重写 */for (depth = 0;; depth++) {struct file *exec;if (depth > 5)return -ELOOP;/* 循环二进制格式处理程序列表,直到识别image */ret = search_binary_handler(bprm);if (ret < 0)return ret;if (!bprm->interpreter)break;exec = bprm->file;bprm->file = bprm->interpreter;bprm->interpreter = NULL;allow_write_access(exec); // i_writecount增加1if (unlikely(bprm->have_execfd)) {if (bprm->executable) { // 传递给解释器的可执行文件,已经存在fput(exec);return -ENOEXEC;}bprm->executable = exec;} elsefput(exec);}audit_bprm(bprm); // 审计结构填充参数// context->type = AUDIT_EXECVE; // 执行参数// context->execve.argc = bprm->argc;trace_sched_process_exec(current, old_pid, bprm);// trace用于跟踪打印信息,俗称“插桩”,通过定义类型、变量和结构,输出相关信息// TRACE_EVENT(sched_process_exec ...ptrace_event(PTRACE_EVENT_EXEC, old_vpid); // 通过SIGTRAP遗留EXEC报告proc_exec_connector(current); // 流程事件连接器return 0;
}

进程:execve加载流程相关推荐

  1. adn进程JAVA_ADN加载流程

    ADN加载流程: IccProvider.java IccProvider定义在/packages/services/Telephony/AndroidManifest.xml android:aut ...

  2. iOS进阶之底层原理-应用程序加载(dyld加载流程、类与分类的加载)

    iOS应用程序的入口是main函数,那么main函数之前系统做了什么呢? 我们定义一个类方法load,打断点,查看栈进程,我们发现dyld做了很多事,接下来就来探究到底dyld做了什么. 什么是dyl ...

  3. Android 系统(169)---Android 7.0 插卡后APN信息的加载流程

    Android 7.0 插卡后APN信息的加载流程.UI界面编辑APN的流程及Android中APN配置相关的漏洞 终端中有一个apns-config.xml文件,负责定义各个运营商规定的默认APN参 ...

  4. Android SIM卡识别加载流程

    文章目录 总述 代码路径 UICC框架 SIM卡识别加载流程 日志分析举例 总述 本文基于Android N(Android 7) 首先要知道SIM卡一般是挂载在CP侧(MODEM侧)的,由MODEM ...

  5. linux驱动加载流程分析

    linux驱动加载流程分析 内核是如何加载驱动的,有些是编译到内核里面,有些事编译成ko,让系统自动加载.总的说来,在Linux下可以通过两种方式加载驱动程序:静态加载和动态加载. 静态加载就是把驱动 ...

  6. Launcher启动流程加载流程学习

     声明: 图片本来是有的 涉及到有些代码不能示人没有贴上,不过仅文字说也足够了,请广大老爷们自行下载源码参看流程分析阅读. 目录 一.认识Launcher: 1 1.功能 1 2.样式 2 3.And ...

  7. Pinpoint Agent加载流程分析

    pinpoint 版本:2.0.3-SNAPSHOT pinpoint利用java agent 特性,提供了一个agent jar包,此jar包会在应用运行之前先运行,agent和应用在同一个进程.p ...

  8. 打卡学习Gradle深度解析 - kts脚本加载流程

    kts脚本加载流程 和groovy脚本一样,kts脚本也分为2个阶段 stage 1 执行buildscript和plugins部分,执行结果会对stage2 program的classpath有影响 ...

  9. QT程序启动加载流程简介

    1. QT应用程序启动加载流程简介 1.1      QWS与QPA启动客户端程序区别 1.1.1   QWS(Qt Window System)介绍 QWS(Qt Windows System)是Q ...

最新文章

  1. 这块抖音爆红的支架,搞到一波优惠,按需而入!
  2. 【原创】Quartus II 实验流程说明书
  3. WCF发布到IIS7问题的解决方案
  4. Python import以及os模块
  5. oracle软件静默安装程序,【oracle】静默安装 oracle 11gr2
  6. python创建数组与列表_python基础(三):数组和列表
  7. python调用usb摄像头黑屏_ORB-SLAM2编译安装和USB摄像头例程运行
  8. 洛谷 P3378 【模板】堆
  9. 高保真原型、动画引导、用户登录、巡检任务、维保任务、用户中心、历史记录、帮助中心、清除缓存、任务抽检、扫描二维码、待办事项、账号设置、客服信息、交互说明、手机端、小程序、app原型、BIM信息综合管理
  10. NUC1657 All in All【字符串匹配】
  11. ASP.NET刷新页面的六种方法(转) 包括在跳转的时候使用提示
  12. jQuery基础之操作属性或文本及其样式 位置 尺寸的方法(重点,常用)
  13. C++封装Detours库挂钩函数
  14. 高级Java程序员必备:《IDEA问题库》常见问题及解决方案,提升开发效率3(JAVA 小虚竹)
  15. 多功能雨伞项目计划书_雨伞策划书范文
  16. 怎么用优动漫PAINT做出色彩的朦胧感?
  17. 未来的计算机范文,未来的电脑作文(通用3篇)
  18. 【挖坑记】JZOJ 4722 跳楼机
  19. 8bit/10bit线路编码简介
  20. 软件测试是什么工作状态,软件测试,现在主要的工作内容是干什么?

热门文章

  1. 洛谷 P1462 通往奥格瑞玛的道路
  2. Python 网络编程(6)总结【转】
  3. 2021年诺贝尔物理学奖揭晓,复杂科学获得重视
  4. 计算机网络原理 - 概述
  5. Android 在xml中更改纯色图片颜色
  6. 知道创宇区块链安全实验室 | OneRing Finance 闪电贷攻击事件分析
  7. ubuntu多屏协同,在电脑上操作手机
  8. 使用install安装应用的时候出现INSTALL_FAILED_SHARED_USER_INCOMPATIBLE问题的解决方法
  9. C语言随机函数为什么rand()每次产生的随机数都一样
  10. OpenGL: 位图字体