午夜精品人妻久久久-成年美女很黄的网站-在线看片免费人成视久网app-国产精品美女无遮挡一区二区-91精品国产综合久久久久-国产的免费视频又猛又爽又刺激-在线看片免费人成视久网app-久久香蕉国产精品视频-av一区二区三区高清

從一個(gè)hello world說起,復(fù)習(xí)C語言程序知識(shí)

從一個(gè)hello world說起
大家好,我是明說網(wǎng)絡(luò)的小明同學(xué) 。今天我們從C語言的Hello World說起,和大家一起溫習(xí)一下C語言中一個(gè)Hello World怎么運(yùn)行起來的,以及C語言如何組織棧緩沖區(qū)等 。本文不適用于C語言初學(xué)者,需要具備有一定的匯編基礎(chǔ) 。好了下面,我們開始吧 。
工具
本文的工具為:
操作系統(tǒng):.04 ,  4.15.0-142-
編譯器:gcc5.4.0( 5.4.0-~16.04.12)
make工具GNU Make 4.1
反匯編查看器:
elf文件查看器:
C語言介紹
C 語言是一種通用的高級(jí)語言 , 最初是由丹尼斯·里奇在貝爾實(shí)驗(yàn)室為開發(fā) UNIX 操作系統(tǒng)而設(shè)計(jì)的 。UNIX 操作系統(tǒng),C編譯器,和幾乎所有的 UNIX 應(yīng)用程序都是用 C 語言編寫的 。由于各種原因,C 語言現(xiàn)在已經(jīng)成為一種廣泛使用的專業(yè)語言 。
同時(shí),C語言是一門大學(xué)期間基本上都會(huì)開設(shè)的課程 。作為一門入門編程課程 , C語言有著獨(dú)特的魅力和不可替代的作用 。雖然當(dāng)前火熱,C語言好像顯得不那么重要了,“難道不香嗎”的疑問開始出現(xiàn) 。但是我的觀點(diǎn)是:每種語言有每種語言的優(yōu)勢(shì),永遠(yuǎn)也取代不了C語言 。像我獨(dú)愛指針,能夠帶來自由的感覺 。
下面就開始我們的探索之旅吧 。
第一個(gè)程序編寫程序
首先我們有如下程序:main.c
//main.c#include int display(char *name){printf("hello world! %sn", name);}int main(){char name[256] = "I'm a string";display(name);return 0;}
上述程序?qū)崿F(xiàn)的功能很簡(jiǎn)單,就是輸出一句話hello world! I'm a ,為了便于說明,其中故意使用了一個(gè)函數(shù)調(diào)用int (char *) 。
函數(shù)的邏輯為,main函數(shù)–> ()函數(shù)(一個(gè)參數(shù))–>函數(shù)(兩個(gè)參數(shù)) 。
是不是很簡(jiǎn)單!
程序編譯
為了便于說明,我們使用文件進(jìn)行編譯 。創(chuàng)建文件名為的文件 , 內(nèi)容如下:
# makefileOBJ=printf.main$(OBJ):gcc main.c -o $@clean:-rm $(OBJ)
我們生成的文件名為.main,這里你可以改為你喜歡的任意名稱 。
使用make命令進(jìn)行編譯,會(huì)生成最終文件 。運(yùn)行后就可以看見hello world! I'm a
小結(jié)
到這里我們就完成了一個(gè)程序的編寫和編譯 , 并且運(yùn)行 。是不是很簡(jiǎn)單 。對(duì)于初學(xué)者,其實(shí)到這里就完了,姑且可以認(rèn)為main函數(shù)就是一個(gè)程序的開始和結(jié)束(我曾經(jīng)就一直這么認(rèn)為) 。但是對(duì)于有過一定經(jīng)驗(yàn)的人來說,就知道:main函數(shù)并不是一個(gè)程序的開始 , 也不是一個(gè)程序的結(jié)束 。
咦 , 這么神奇的嗎?就讓我們來看看吧 。
【從一個(gè)hello world說起,復(fù)習(xí)C語言程序知識(shí)】Hello world 的背后
首先讓我們來認(rèn)識(shí)一下我們生成的.main 。
file ./printf.main ./printf.main: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=5c389a402866aaa012b8b8ab992fed778eb989b0, not stripped
ELF是執(zhí)行和鏈接格式( and)的縮略詞 。它是UNIX系統(tǒng)的幾種可執(zhí)行文件格式中的一種 。
使用命令 -h ./.main > .txt
ELF Header:Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64Data: 2's complement, little endianVersion: 1 (current)OS/ABI: UNIX - System VABI Version: 0Type: EXEC (Executable file)Machine: Advanced Micro Devices X86-64Version: 0x1Entry point address: 0x4004a0 //注意這一行Start of program headers: 64 (bytes into file)Start of section headers: 6712 (bytes into file)Flags: 0x0Size of this header: 64 (bytes)Size of program headers: 56 (bytes)Number of program headers: 9Size of section headers: 64 (bytes)Number of section headers: 31Section header string table index: 28
這里面,我們注意第11行 , Entry point : ,顯示,入口點(diǎn)地址為 , 說明操作系統(tǒng)在運(yùn)行這個(gè).main程序時(shí),首先從這個(gè)地址開始運(yùn)行 。那么我們看看這個(gè)地址到底是什么吧

從一個(gè)hello world說起,復(fù)習(xí)C語言程序知識(shí)

文章插圖
從一個(gè)hello world說起,復(fù)習(xí)C語言程序知識(shí)

文章插圖
匯編
使用命令 -d .main > .txt將程序的匯編代碼提取出來(刪除了一些當(dāng)前沒有必要說明的內(nèi)容),如下所示:
printf.main: file format elf64-x86-64Disassembly of section .init:0000000000400428 :400428: 48 83 ec 08 sub $0x8,%rsp40042c: 48 8b 05 c5 0b 20 00 mov 0x200bc5(%rip),%rax # 600ff8 400433: 48 85 c0 test %rax,%rax400436: 74 05 je 40043d 400438: e8 53 00 00 00 callq 400490 40043d: 48 83 c4 08 add $0x8,%rsp400441: c3 retq Disassembly of section .plt:0000000000400470 :400470: ff 25 aa 0b 20 00 jmpq *0x200baa(%rip) # 601020 400476: 68 01 00 00 00 pushq $0x140047b: e9 d0 ff ff ff jmpq 400450 0000000000400480 :400480: ff 25 a2 0b 20 00 jmpq *0x200ba2(%rip) # 601028 400486: 68 02 00 00 00 pushq $0x240048b: e9 c0 ff ff ff jmpq 400450 Disassembly of section .plt.got:0000000000400490 :400490: ff 25 62 0b 20 00 jmpq *0x200b62(%rip) # 600ff8 400496: 66 90 xchg %ax,%axDisassembly of section .text:00000000004004a0 :4004a0: 31 ed xor %ebp,%ebp4004a2: 49 89 d1 mov %rdx,%r94004a5: 5e pop %rsi4004a6: 48 89 e2 mov %rsp,%rdx4004a9: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp4004ad: 50 push %rax4004ae: 54 push %rsp4004af: 49 c7 c0 b0 06 40 00 mov $0x4006b0,%r8 //00000000004006b0 :4004b6: 48 c7 c1 40 06 40 00 mov $0x400640,%rcx //0000000000400640 :4004bd: 48 c7 c7 bb 05 40 00 mov $0x4005bb,%rdi //00000000004005bb
:4004c4: e8 b7 ff ff ff callq 400480 4004c9: f4 hlt 4004ca: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)0000000000400596 :400596: 55 push %rbp400597: 48 89 e5 mov %rsp,%rbp40059a: 48 83 ec 10 sub $0x10,%rsp40059e: 48 89 7d f8 mov %rdi,-0x8(%rbp)4005a2: 48 8b 45 f8 mov -0x8(%rbp),%rax4005a6: 48 89 c6 mov %rax,%rsi4005a9: bf c4 06 40 00 mov $0x4006c4,%edi4005ae: b8 00 00 00 00 mov $0x0,%eax4005b3: e8 b8 fe ff ff callq 400470 4005b8: 90 nop4005b9: c9 leaveq 4005ba: c3 retq 00000000004005bb
:4005bb: 55 push %rbp4005bc: 48 89 e5 mov %rsp,%rbp4005bf: 48 81 ec 10 01 00 00 sub $0x110,%rsp4005c6: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax4005cd: 00 00 4005cf: 48 89 45 f8 mov %rax,-0x8(%rbp)4005d3: 31 c0 xor %eax,%eax4005d5: 48 b8 49 27 6d 20 61 movabs $0x74732061206d2749,%rax4005dc: 20 73 74 4005df: 48 89 85 f0 fe ff ff mov %rax,-0x110(%rbp)4005e6: 48 c7 85 f8 fe ff ff movq $0x676e6972,-0x108(%rbp)4005ed: 72 69 6e 67 4005f1: 48 8d 95 00 ff ff ff lea -0x100(%rbp),%rdx4005f8: b8 00 00 00 00 mov $0x0,%eax4005fd: b9 1e 00 00 00 mov $0x1e,%ecx400602: 48 89 d7 mov %rdx,%rdi400605: f3 48 ab rep stos %rax,%es:(%rdi)400608: 48 8d 85 f0 fe ff ff lea -0x110(%rbp),%rax40060f: 48 89 c7 mov %rax,%rdi400612: e8 7f ff ff ff callq 400596 400617: b8 00 00 00 00 mov $0x0,%eax40061c: 48 8b 75 f8 mov -0x8(%rbp),%rsi400620: 64 48 33 34 25 28 00 xor %fs:0x28,%rsi400627: 00 00 400629: 74 05 je 400630
40062b: e8 30 fe ff ff callq 400460 400630: c9 leaveq 400631: c3 retq 400632: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)400639: 00 00 00 40063c: 0f 1f 40 00 nopl 0x0(%rax)0000000000400640 :400640: 41 57 push %r15400642: 41 56 push %r14400644: 41 89 ff mov %edi,%r15d400647: 41 55 push %r13400649: 41 54 push %r1240064b: 4c 8d 25 be 07 20 00 lea 0x2007be(%rip),%r12 # 600e10 400652: 55 push %rbp400653: 48 8d 2d be 07 20 00 lea 0x2007be(%rip),%rbp # 600e18 40065a: 53 push %rbx40065b: 49 89 f6 mov %rsi,%r1440065e: 49 89 d5 mov %rdx,%r13400661: 4c 29 e5 sub %r12,%rbp400664: 48 83 ec 08 sub $0x8,%rsp400668: 48 c1 fd 03 sar $0x3,%rbp40066c: e8 b7 fd ff ff callq 400428 400671: 48 85 ed test %rbp,%rbp400674: 74 20 je 400696 400676: 31 db xor %ebx,%ebx400678: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)40067f: 00 400680: 4c 89 ea mov %r13,%rdx400683: 4c 89 f6 mov %r14,%rsi400686: 44 89 ff mov %r15d,%edi400689: 41 ff 14 dc callq *(%r12,%rbx,8)40068d: 48 83 c3 01 add $0x1,%rbx400691: 48 39 eb cmp %rbp,%rbx400694: 75 ea jne 400680 400696: 48 83 c4 08 add $0x8,%rsp40069a: 5b pop %rbx40069b: 5d pop %rbp40069c: 41 5c pop %r1240069e: 41 5d pop %r134006a0: 41 5e pop %r144006a2: 41 5f pop %r154006a4: c3 retq 4006a5: 90 nop4006a6: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)4006ad: 00 00 00 00000000004006b0 :4006b0: f3 c3 repz retq Disassembly of section .fini:00000000004006b4 :4006b4: 48 83 ec 08 sub $0x8,%rsp4006b8: 48 83 c4 08 add $0x8,%rsp4006bc: c3 retq
這里我們注意第45,46,47 , 48行,注意其中
4004af: 49 c7 c0 b0 06 40 00 mov $0x4006b0,%r8 //00000000004006b0 :4004b6: 48 c7 c1 40 06 40 00 mov $0x400640,%rcx //0000000000400640 :4004bd: 48 c7 c7 bb 05 40 00 mov $0x4005bb,%rdi //00000000004005bb
:4004c4: e8 b7 ff ff ff callq 400480
@plt包含了三個(gè)參數(shù),,,main顯然,從名稱上就可以看出這四個(gè)函數(shù)的作用 。
是libc.so.6中的一個(gè)函數(shù) 。它的原型是這樣的:
extern int BP_SYM (__libc_start_main) (int (*main) (int, char **, char **),int argc,char *__unbounded *__unbounded ubp_av,void (*init) (void),void (*fini) (void),void (*rtld_fini) (void),void *__unbounded stack_end)__attribute__ ((noreturn));
這個(gè)函數(shù)需要做的是建立/初始化一些數(shù)據(jù)結(jié)構(gòu)/環(huán)境然后調(diào)用我們的main() 。
程序啟動(dòng)的過程應(yīng)該: ->->-> _init -> main -> _fini.
這篇文章有詳細(xì)的說明:linux編程之main()函數(shù)啟動(dòng)過程
棧緩沖區(qū)及結(jié)構(gòu)
有16個(gè)64位寄存器,分別是:
%rax,%rbx,%rcx,%rdx,%esi,%edi , %rbp,%rsp,%r8,%r9,%r10,%r11,%r12 , %r13,%r14 , %r15 。
其中:
%rax 作為函數(shù)返回值使用 。%rsp 棧指針寄存器,指向棧頂 %rdi,%rsi,%rdx,%rcx,%r8,%r9 用作函數(shù)參數(shù)考勤系統(tǒng)的c語言源代碼,依次對(duì)應(yīng)第1參數(shù),第2參數(shù) %rbx,%rbp,%r12,%r13,, 用作數(shù)據(jù)存儲(chǔ),遵循被調(diào)用者使用規(guī)則,簡(jiǎn)單說就是隨便用,調(diào)用子函數(shù)之前要備份它,以防他被修改 %r10,%r11 用作數(shù)據(jù)存儲(chǔ) , 遵循調(diào)用者使用規(guī)則,簡(jiǎn)單說就是使用之前要先保存原值
64位與32位的不同在于64位不用壓棧來存儲(chǔ)下一個(gè)函數(shù)參數(shù),而是放在了%rdi,%rsi,%rdx,%rcx,%r8,%r9六個(gè)寄存器中,超出部分再壓棧 。
首先,我們將main.c文件進(jìn)行匯編,使用命令gcc -S main.c , 在當(dāng)前目錄下會(huì)生成main.s的匯編文件,內(nèi)容如下:
.file "main.c".section .rodata.LC0:.string "hello world! %sn".text.globl display.type display, @functiondisplay:.LFB0:.cfi_startprocpushq %rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq %rsp, %rbp.cfi_def_cfa_register 6subq $16, %rspmovq %rdi, -8(%rbp)movq -8(%rbp), %raxmovq %rax, %rsimovl $.LC0, %edimovl $0, %eaxcall printfnopleave.cfi_def_cfa 7, 8ret.cfi_endproc.LFE0:.size display, .-display.globl main.type main, @functionmain:.LFB1:.cfi_startprocpushq %rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq %rsp, %rbp.cfi_def_cfa_register 6subq $272, %rspmovq %fs:40, %raxmovq %rax, -8(%rbp)xorl %eax, %eaxmovabsq $8391086132249306953, %rax //0x74732061206d2749 ("I'm a st")movq %rax, -272(%rbp)movq $1735289202, -264(%rbp)leaq -256(%rbp), %rdxmovl $0, %eaxmovl $30, %ecxmovq %rdx, %rdirep stosqleaq -272(%rbp), %raxmovq %rax, %rdi //使用%rdi寄存器壓入?yún)?shù)call display //調(diào)用函數(shù)movl $0, %eaxmovq -8(%rbp), %rsixorq %fs:40, %rsije .L4call __stack_chk_fail.L4:leave.cfi_def_cfa 7, 8ret.cfi_endproc.LFE1:.size main, .-main.ident "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609".section .note.GNU-stack,"",@progbits
main函數(shù)
在53,54行,使用rdi壓入了一個(gè)參數(shù),參數(shù)的地址在-272(%rbp)(即rdi), 可以看出正好是字符串”I'm a “的地址 。如下所示:
其中,函數(shù)調(diào)用棧緩沖區(qū)顯示當(dāng)前棧緩沖區(qū)為main,再上一層為,再次印證了上一節(jié)的說法 。
函數(shù)
下面我們進(jìn)入函數(shù),可以看出的兩個(gè)參數(shù)分別放在rdi,rsi兩個(gè)寄存器當(dāng)中 。
其中,函數(shù)調(diào)用棧緩沖區(qū)顯示當(dāng)前棧緩沖區(qū)為,再上一層為main,,再次印證了上一節(jié)的說法 。
小結(jié)
通過對(duì)main函數(shù)中函數(shù)的參數(shù),函數(shù)中的函數(shù)的參數(shù)進(jìn)行實(shí)驗(yàn),說明了C語言在函數(shù)調(diào)用時(shí)的棧緩沖區(qū)的組織 。
結(jié)語
對(duì)于一個(gè)普普通通的C語言程序 , 其實(shí)其背后是一堆復(fù)雜的操作系統(tǒng)預(yù)備好的操作,執(zhí)行完畢之后,就開始執(zhí)行我們的main函數(shù) 。main函數(shù)并不是程序執(zhí)行的第一個(gè)函數(shù),當(dāng)然也不是最后一個(gè) 。我們編寫的程序的main函數(shù),僅僅是操作系統(tǒng)在加載elf文件時(shí)候調(diào)用的函數(shù)而已考勤系統(tǒng)的c語言源代碼 , 僅僅是函數(shù)而已 。
棧緩沖區(qū)的組織,一定要?jiǎng)邮肿约赫{(diào)一調(diào),理解棧緩沖區(qū),有助于理解pwn題中的棧緩沖的利用 。
這就是我喜歡C語言的原因 , 因?yàn)樗茏屛腋忧逦乜吹匠绦蜻\(yùn)行的背后,而像這類語言,我也使用,因?yàn)檎娴姆奖?,但是?duì)于理解計(jì)算機(jī)、理解背后的故事非常的不利 。
我,學(xué)習(xí)更多系統(tǒng)的知識(shí)!
本文到此結(jié)束 , 希望對(duì)大家有所幫助 。