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這個頭文件