SYSCALL_DEFINE3(execve, const char __user *, filename, const char __user *const __user *, argv, const char __user *const __user *, envp) { return do_execve(getname(filename), argv, envp); }可以看到在系统调用execve的定义中,设置了三个参数:将要执行的可执行文件的文件名;指向参数表的指针;环境变量。在函数体中直接转去执行内核函数 do_execve(),
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_execve_common(filename, argv, envp); }从代码可以看出,在do_execve()中将execve传递过来的向量表中的变量接收到了user_arg_ptr结构体变量argv和envp中然后又去调用了内核函数do_execve_common()
static int do_execve_common(struct filename *filename,
struct user_arg_ptr argv,
struct user_arg_ptr envp)
{
struct linux_binprm *bprm; //这个结构体用于保存加载二进制文件时的一些参数
struct file *file; //文件指针
struct files_struct *displaced; //open file table structure
int retval;//返回值
参数检查:
if (IS_ERR(filename))
return PTR_ERR(filename);/*We move the actual failure in case of RLIMIT_NPROC excess from set*uid() to execve()
because too many poorly written programs don't check setuid() return code.
Here we additionally recheck whether NPROC limit is still exceeded.*/
if ((current->flags & PF_NPROC_EXCEEDED) && atomic_read(¤t_user()->processes) > rlimit(RLIMIT_NPROC)) {
retval = -EAGAIN;
goto out_ret;
}
/* We're below the limit (still or again), so we don't want to make further execve() calls fail. */
current->flags &= ~PF_NPROC_EXCEEDED; //#define PF_NPROC_EXCEEDED 0x00001000 /* set_user noticed that RLIMIT_NPROC was exceeded */ retval = unshare_files(&displaced);// 不共享文件,将文件重新复制一份 if (retval) goto out_ret;
retval = -ENOMEM; bprm = kzalloc(sizeof(*bprm), GFP_KERNEL); 分配内存,GFP_KERNEL表示分配的内存类型为:普通的内核ram if (!bprm) goto out_files; retval = prepare_bprm_creds(bprm); 准备凭证 if (retval) goto out_free; check_unsafe_exec(bprm); //determine how safe it is to execute the proposed program current->in_execve = 1; file = do_open_exec(filename); 打开将要运行的文件:1、判断文件是否可执行 2、标记文件已经被打开 3、禁止对文件写入 retval = PTR_ERR(file); if (IS_ERR(file)) goto out_unmark; sched_exec(); //分配cpu bprm->file = file; bprm->filename = bprm->interp = filename->name; retval = bprm_mm_init(bprm); //初始化内存单元 if (retval) goto out_unmark; bprm->argc = count(argv, MAX_ARG_STRINGS);// 统计参数个数 并做简单的检查 if ((retval = bprm->argc) < 0) goto out; bprm->envc = count(envp, MAX_ARG_STRINGS); //统计环境变量的个数,并做简单检查
if ((retval = bprm->envc) < 0) goto out; retval = prepare_binprm(bprm);// -> int cap_bprm_set_creds(struct linux_binprm *bprm)(链接)准备被二进制文件运行的凭证 if (retval < 0) goto out;
复制参数: retval = copy_strings_kernel(1, &bprm->filename, bprm); if (retval < 0) goto out; bprm->exec = bprm->p; retval = copy_strings(bprm->envc, envp, bprm); if (retval < 0) goto out; retval = copy_strings(bprm->argc, argv, bprm); if (retval < 0) goto out;
retval = exec_binprm(bprm); //==>search_binary_handler() 遍历二进制文件处理工具链表,找到解析该二进制文件的handler,并初始化 if (retval < 0) goto out; /* execve succeeded */ current->fs->in_exec = 0; current->in_execve = 0; // Tell the LSMs that the process is doing an execve */ acct_update_integrals(current); // 更task_struct中完整的内存管理单元区域,根据可执行文件的文件的inode. //释放资源
task_numa_free(current);
free_bprm(bprm);
putname(filename);if (displaced)put_files_struct(displaced);
return
retval
;
出错处理: out: if (bprm->mm) { acct_arg_size(bprm, 0); mmput(bprm->mm); } out_unmark: current->fs->in_exec = 0; current->in_execve = 0; out_free: free_bprm(bprm); out_files: if (displaced) reset_files_struct(displaced); out_ret: putname(filename); return retval; }
exec_binprm()函数中有调用了search_binary_handler()函数:
int search_binary_handler(struct linux_binprm *bprm) 1353{ 1354 bool need_retry = IS_ENABLED(CONFIG_MODULES); 1355 struct linux_binfmt *fmt; 1356 int retval; 1357 1358 /* This allows 4 levels of binfmt rewrites before failing hard. */ 1359 if (bprm->recursion_depth > 5) 1360 return -ELOOP; 1361 1362 retval = security_bprm_check(bprm); 1363 if (retval) 1364 return retval; 1365 1366 retval = -ENOENT; 1367 retry: 1368 read_lock(&binfmt_lock); 1369 list_for_each_entry(fmt, &formats, lh) {
//在这里遍历handler链表, fmt 变量是一个linux_binfmt结构体,
在这个结构体中有一个load_binary成员,再找到能够处理对应格式二进制文件的handler后,将load_binary成员指向对应handler, 1370 if (!try_module_get(fmt->module)) 1371 continue; 1372 read_unlock(&binfmt_lock); 1373 bprm->recursion_depth++; 1374 retval = fmt->load_binary(bprm); //然后用这个handler来处理该二进制文件 在load_binary() 的最后,
会调用 start_thread() 函数重新设置EIP和ESP 将程序强行转到要调用的可执行程序的入口区执行 1375 read_lock(&binfmt_lock); 1376 put_binfmt(fmt); 1377 bprm->recursion_depth--; 1378 if (retval < 0 && !bprm->mm) { 1379 /* we got to flush_old_exec() and failed after it */ 1380 read_unlock(&binfmt_lock); 1381 force_sigsegv(SIGSEGV, current); 1382 return retval; 1383 } 1384 if (retval != -ENOEXEC || !bprm->file) { 1385 read_unlock(&binfmt_lock); 1386 return retval; 1387 } 1388 } 1389 read_unlock(&binfmt_lock); 1390 1391 if (need_retry) { 1392 if (printable(bprm->buf[0]) && printable(bprm->buf[1]) && 1393 printable(bprm->buf[2]) && printable(bprm->buf[3])) 1394 return retval; 1395 if (request_module("binfmt-%04x", *(ushort *)(bprm->buf + 2)) < 0) 1396 return retval; 1397 need_retry = false; 1398 goto retry; 1399 } 1400 1401 return retval; 1402}
查看函数exec_binprm代码,这段代码将父进程的pid保存,获取新的pid,后执行search_binary_hander(bprm),用来遍历format链表,找到合适的处理hello的handler。