1. C语言可变参数及其原理
初学C语言时,面对`printf`函数能够处理不同数量参数的机制,我有过疑问。追踪`printf`的实现,涉及`__builtin_va_list`、`__builtin_va_start`、`__builtin_va_end`等函数,以及调用`__mingw_vprintf`。在linux内核代码中找到了`printk`的实现,其与`printf`使用方法相似,提供了可变参数的关键代码,清晰明了。通过内嵌汇编,我关注了`va_start`、`vsprintf`调用和`va_end`。`va_start`和`va_end`定义在`include/stdarg.h`中。
`va_start(ap,v)`宏将`ap`设置为`v`的第一个参数地址后加上参数类型大小,`ap=(char*)&v+_INTSIZEOF(v)`。`_INTSIZEOF(n)`宏计算`n`参数在栈中的实际大小,`((sizeof(n)+sizeof(int)-1)&~(sizeof(int)-1))`,确保存储空间是`int`类型长度的整数倍。
例如,对于`char`、`short`、`int`、`double`类型,计算结果分别是4字节,这确保了参数在栈上的正确对齐。
`va_arg(ap,t)`宏从参数列表中获取类型为`t`的参数,`(*(t*)((ap+=_INTSIZEOF(t))-_INTSIZEOF(t)))`。`va_end`宏将`ap`设置为空指针。
通过这些宏,`vsprintf`函数能够通过格式字符串逐个获取参数,转换成字符,放入缓存中,并通过系统接口输出。通过格式化字符串获取栈上内容,理论上可以故意写错格式化字符串导致崩溃。特别的是,`%n`格式符不输出任何内容,但在输出字符数量后将此值存入指定位置。在无栈保护机制的情况下,不当的格式化字符串可能导致栈被用户直接访问,如果控制输出的字符数量,可以将特定指令地址写入关键位置,从而实现攻击。
理解`printf`和`printk`实现的细节,不仅有助于深入掌握C语言的底层机制,还能增强对安全编程的意识,避免潜在的漏洞利用风险。
2. 在Linux环境下使用C语言进行编程,题目要求如下
nt S=1; //S 表示盘子是否为空;
int Sa=0; //Sa表示盘中是否有苹果;
int Sb=0; //Sb表示盘中是否有桔子;
父亲
while(TRUE)
{
Wait(S);
将水果放入版盘中
;
if (
放入的是权桔子
)
Signal(Sb);
Else
Signal(Sa);
}
儿子
while(TRUE)
{
Wait(Sb);
从盘中取出桔子;
Signal(S);
吃桔子
;
}
女儿
while(TRUE)
{
Wait(Sa);
从盘中取出苹果;
Signal(S);
吃苹果
;
}
3. linux编译的main函数的参数是怎么传值进来
方法1.
C/C++语言中的main函数,经常带有参数argc,argv,如下:
int main(int argc, char** argv)
int main(int argc, char* argv[])
这两个参数的作用是什么呢?argc 是指命令行输入参数的个数,argv存储了所有的命令行参数。假如你的程序是hello.exe,如果在命令行运行该程序,(首先应该在命令行下用 cd 命令进入到 hello.exe 文件所在目录) 运行命令为:
hello.exe Shiqi Yu
那么,argc的值是 3,argv[0]是"hello.exe",argv[1]是"Shiqi",argv[2]是"Yu"。
下面的程序演示argc和argv的使用:
4. Linux GCC 如何查看及指定 C 语言标准
在Linux系统中,查看和指定C语言标准对于编程实践尤为重要。常见的C语言标准包括C89、C99、C11和C17。
要了解当前支持的标准,只需运行gcc命令。输出结果与C标准对应,例如#define __STDC_VERSION__ 199901L表示C99标准,#define __STDC_VERSION__ 201112L表示C11标准,#define __STDC_VERSION__ 201710L表示C17标准,若未查到,则默认为C89标准。
若需在编译时指定C语言标准,使用-std选项参数。Linux默认使用-std=gnu11,即C11标准加上GCC扩展。
假设程序main.c如下,若指定C89标准进行编译,将遇到错误。这是因为C89标准不支持在for循环中声明变量i。若改为C99标准再次编译,则问题解决。
5. Linux C语言用system()调用,带参数
linux c system函数介绍:
system(执行shell 命令)
相关函数
fork,execve,waitpid,popen
表头文件
# nclude
定义函数
int system(const char * string);
函数说明
system()会调用fork()产生子进程,由子进程来调用/bin/sh-c string来执行参数string字符串所代表的命令,此命>令执行完后随即返回原调用的进程。在调用system()期间SIGCHLD 信号会被暂时搁置,SIGINT和SIGQUIT 信号则会被忽略。
返回值
=-1:出现错误
=0:调用成功但是没有出现子进程
>0:成功退出的子进程的id
如果system()在调用/bin/sh时失败则返回127,其他失败原因返回-1。若参数string为空指针(NULL),则返回非零值>。如果system()调用成功则最后会返回执行shell命令后的返回值,但是此返回值也有可能为 system()调用/bin/sh失败所返回的127,因此最好能再检查errno 来确认执行成功。
附加说明
在编写具有SUID/SGID权限的程序时请勿使用system(),system()会继承环境变量,通过环境变量可能会造成系统安全的问题。
范例
#i nclude
main()
{
system("ls -al /etc/passwd /etc/shadow");
}
执行结果:
-rw-r--r-- 1 root root 705 Sep 3 13 :52 /etc/passwd
-r--------- 1 root root 572 Sep 2 15 :34 /etc/shado
例2:
char tmp[];
sprintf(tmp,"/bin/mount -t vfat %s /mnt/usb",dev);
system(tmp);
其中dev是/dev/sda1。
system函数的源码
#include <syspes.h>
#include <sys/wait.h>
#include <errno.h>
#include <unistd.h>
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的返回值呢??
char buf[10];
char * ps="ps -ef|grep -c root";
FILE *ptr;
int i;
if((ptr = popen(ps, "r")) != NULL)
{
fgets(buf, 10 , ptr);
i = atoi(buf);
pclose(ptr);
}
可以man下waitpid查看下如何检查status的值
int ret = system("ls -al /etc/passwd /etc/shadow");
if(WIFSIGNALED(ret))
具体的这些宏查看man waitpid
6. 在linux下,怎么使用C语言编写程序自动修改网络参数谢谢~!
你可以调用 系统 system函数,在程序里执行shell,如果想永久生效网路配置,示例如下 system("echo DEVICE=eth0 > /etc/sysconfig/network-scripts/ifcfg-eth0");
system("echo ONBOOT=yes >> /etc/sysconfig/network-scripts/ifcfg-eth0");
system("echo BOOTPROTO=static >> /etc/sysconfig/network-scripts/ifcfg-eth0");
system("echo IPADDR=192.168.10.10 >> /etc/sysconfig/network-scripts/ifcfg-eth0");
system("echo NETMASK=255.255.255.0 >> /etc/sysconfig/network-scripts/ifcfg-eth0"); system("/sbin/service network restart");
exit(0);忘了说了,记得包含stdlib.h这个头文件