网址导航网站,毕业设计做网站 服务器怎么弄,静态网站更新文章麻烦,花都区建设局网站文章目录system函数函数原型功能参数返回值返回值获取shell退出状态实现原理安全风险system运行任意的shell命令命令注入漏洞exec函数族核心功能函数原型命名规律参数对比返回值常见错误码应用例程execl() - 参数列表形式execv() - 参数数组形式execle() - 自定义环境变量execv…文章目录system函数函数原型功能参数返回值返回值获取shell退出状态实现原理安全风险system运行任意的shell命令命令注入漏洞exec函数族核心功能函数原型命名规律参数对比返回值常见错误码应用例程execl() - 参数列表形式execv() - 参数数组形式execle() - 自定义环境变量execve() - 系统调用最底层execlp() - 自动搜索PATHexecvp() - PATH搜索 参数数组标准模式fork execsystem函数函数原型#includestdlib.hintsystem(constchar*command);功能执行shell命令就像在终端中输入命令一样system(ls -la);// 执行ls命令system(echo Hello);// 执行echo命令system(gcc test.c);// 执行编译命令参数有效命令执行命令NULL检查shell是否可用空字符串直接返回成功#includestdlib.h#includestdio.hintmain(){// 1. 执行简单命令system(pwd);// 打印当前目录// 2. 执行带参数的命令system(ls -l /tmp);// 3. 执行复杂命令管道、重定向等system(ps aux | grep bash output.txt);// 4. 使用变量构建命令charfilename[100]test.txt;charcommand[200];snprintf(command,sizeof(command),cat %s,filename);system(command);return0;}返回值 127启动shell失败/bin/sh 不存在或不可执行 -1其他错误fork失败、内存不足等其他值命令执行状态shell的退出状态编码intretsystem(command);if(ret-1){// system()调用本身失败perror(system() failed);}elseif(WIFEXITED(ret)){// 命令正常结束printf(命令退出码: %d\n,WEXITSTATUS(ret));if(WEXITSTATUS(ret)127){printf(错误: shell无法启动或命令不存在\n);}elseif(WEXITSTATUS(ret)126){printf(错误: 命令不可执行\n);}}elseif(WIFSIGNALED(ret)){// 命令被信号终止printf(命令被信号终止: %d\n,WTERMSIG(ret));}返回值获取shell退出状态// 要获取真正的退出状态需要处理返回值intstatussystem(ls /nonexistent);if(status-1){printf(system()调用失败\n);}else{// 使用wait相关的宏if(WIFEXITED(status)){intexit_codeWEXITSTATUS(status);printf(命令退出码: %d\n,exit_code);// ls /nonexistent 会返回 2}}实现原理system(ls -l) ↓ fork() 创建子进程 ↓ 子进程execl(/bin/sh, sh, -c, ls -l, NULL) ↓ shell解析命令ls -l ↓ shell执行ls命令 ↓ shell退出返回ls的退出状态 ↓ 父进程接收状态返回给调用者// system() 大致相当于以下代码intsystem(constchar*command){if(commandNULL){// 检查shell是否存在returnshell_exists()?1:0;}pid_tpidfork();if(pid0){// 子进程执行shellexecl(/bin/sh,sh,-c,command,(char*)NULL);_exit(127);// 如果execl失败}elseif(pid0){// 父进程等待子进程结束intstatus;waitpid(pid,status,0);returnstatus;// 返回shell的退出状态}else{// fork失败return-1;}}安全风险system运行任意的shell命令#includestdio.h#includestdlib.hintmain(intargc,constchar*argv[]){charbuf[1024]{};while(1){printf(命令-);fgets(buf,1024,stdin);system(buf);}return0;}命令注入漏洞如果键入rm -rf *;会造成不可挽回的后果// ⚠️ 危险可能被攻击charuser_input[100];printf(输入文件名: );fgets(user_input,sizeof(user_input),stdin);charcommand[200];sprintf(command,rm %s,user_input);// ⚠️ 危险system(command);// 如果用户输入: test.txt; rm -rf /// 实际执行: rm test.txt; rm -rf /exec函数族核心功能进程替换不创建新进程替换当前进程的代码和数据继承PID保持原进程ID不变全新开始从新程序的main()函数开始执行函数原型// 所有exec函数都在 unistd.h 中#includeunistd.hintexecl(constchar*path,constchar*arg0,...,(char*)NULL);intexecv(constchar*path,char*constargv[]);intexecle(constchar*path,constchar*arg0,...,(char*)NULL,char*constenvp[]);intexecve(constchar*path,char*constargv[],char*constenvp[]);intexeclp(constchar*file,constchar*arg0,...,(char*)NULL);intexecvp(constchar*file,char*constargv[]);命名规律execl - l: list (参数列表) execv - v: vector (参数数组) execle - e: environment (传递环境变量) execlp - p: PATH (在PATH中查找程序) execvp - p v: PATH 参数数组 execve - 系统调用 (v e)参数对比函数程序路径参数传递方式环境变量PATH搜索execl()完整路径参数列表继承当前否execv()完整路径参数数组继承当前否execle()完整路径参数列表自定义否execve()完整路径参数数组自定义否execlp()文件名参数列表继承当前是execvp()文件名参数数组继承当前是返回值执行成功不返回进程被替换执行失败返回 -1设置 errno// 重要exec成功时后面的代码不会执行if(execl(/bin/ls,ls,NULL)-1){// 只有exec失败时才会执行到这里perror(exec failed);exit(1);}// exec成功后这里的代码永远不会执行printf(这行不会打印\n);常见错误码#includeerrno.hintretexecvp(nonexistent,args);if(ret-1){switch(errno){caseENOENT:printf(程序不存在\n);break;caseEACCES:printf(没有执行权限\n);break;caseENOMEM:printf(内存不足\n);break;caseE2BIG:printf(参数列表太长\n);break;caseENOEXEC:printf(不是可执行文件\n);break;default:perror(exec失败);}exit(1);}应用例程execl() - 参数列表形式intexecl(constchar*path,constchar*arg0,...,(char*)NULL);// 参数以列表形式传递以NULL结束// 示例执行 ls -l /tmpexecl(/bin/ls,ls,-l,/tmp,NULL);// 相当于终端命令$ ls -l /tmpexecv() - 参数数组形式intexecv(constchar*path,char*constargv[]);// 参数以数组形式传递// 示例执行 ls -l /tmpchar*args[]{ls,-l,/tmp,NULL};execv(/bin/ls,args);// argv数组必须以NULL结束execle() - 自定义环境变量intexecle(constchar*path,constchar*arg0,...,(char*)NULL,char*constenvp[]);// 示例执行程序并传递新环境变量char*env[]{MYVARhello,PATH/usr/bin,NULL};execle(/bin/sh,sh,-c,echo $MYVAR,NULL,env);execve() - 系统调用最底层intexecve(constchar*path,char*constargv[],char*constenvp[]);// 示例完全控制参数和环境变量char*args[]{myprog,-v,NULL};char*env[]{DEBUG1,LOG_LEVELdebug,NULL};execve(/usr/local/bin/myprog,args,env);execlp() - 自动搜索PATHintexeclp(constchar*file,constchar*arg0,...,(char*)NULL);// 示例不需要完整路径execlp(ls,ls,-l,/tmp,NULL);// 系统会在PATH中查找ls// 相当于execl(/bin/ls, ls, -l, /tmp, NULL);execvp() - PATH搜索 参数数组intexecvp(constchar*file,char*constargv[]);// 示例最常用的组合char*args[]{gcc,test.c,-o,test,NULL};execvp(gcc,args);// 自动在PATH中找gcc标准模式fork exec#includeunistd.h#includesys/wait.h#includestdio.h#includestdlib.hintmain(){pid_tpidfork();if(pid-1){perror(fork失败);exit(1);}if(pid0){// 子进程执行新程序printf(子进程 PID%d 准备执行新程序\n,getpid());// 方法1使用execlexecl(/bin/ls,ls,-l,/tmp,NULL);// 方法2使用execvp// char *args[] {ls, -l, /tmp, NULL};// execvp(ls, args);// 如果exec失败perror(exec失败);_exit(1);// 子进程结束}else{// 父进程等待子进程printf(父进程 PID%d 等待子进程\n,getpid());intstatus;waitpid(pid,status,0);if(WIFEXITED(status)){printf(子进程退出码: %d\n,WEXITSTATUS(status));}}return0;}资源在exec后保持不变进程ID和父进程ID实际用户ID和实际组ID进程组ID和会话ID控制终端当前工作目录文件创建掩码文件锁未处理的闹钟信号处理方式但被忽略的信号保持忽略资源在exec后变化代码段、数据段、堆栈全新文件描述符的close-on-exec标志共享内存、内存映射文件线程所有线程终止