成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

CTF--PWN必備技能--理解c程序從編譯開始到運(yùn)行結(jié)束的過程-創(chuàng)新互聯(lián)

重溫c語言

我們在linux平臺(tái)下建立一個(gè)a.c文件,程序很簡單,顯示輸出Please input your name:,然后讓我們輸入名字,最后調(diào)用了一個(gè)子函數(shù)輸出hello,我們的名字

成都創(chuàng)新互聯(lián)公司作為成都網(wǎng)站建設(shè)公司,專注成都網(wǎng)站建設(shè)公司、網(wǎng)站設(shè)計(jì),有關(guān)成都定制網(wǎng)站方案、改版、費(fèi)用等問題,行業(yè)涉及不銹鋼雕塑等多個(gè)領(lǐng)域,已為上千家企業(yè)服務(wù),得到了客戶的尊重與認(rèn)可。
#includevoid hello(char * name);
	int main()
	{char name[16]={0};
	        printf("Please input your name:");
	        gets(name);
	        hello(name);
	        return 0;
	}
	void hello(char * name)
	{printf("hello,%s\n",name);
	}
從.c文件到可執(zhí)行文件 gcc的編譯過程
預(yù)處理(Preprocessing),
編譯(Compilation),
匯編(Assemble),
鏈接(Linking)

gcc編譯C語言主要用到以下幾個(gè)程序:C編譯器gcc、匯編器as、鏈接器ld和二進(jìn)制轉(zhuǎn)換工具objcopy

gcc選項(xiàng)總結(jié):
-E 只激活預(yù)處理,這個(gè)不生成文件,你需要把它重定向到一個(gè)輸出文件里面
-S 編譯到匯編語言不進(jìn)行匯編和鏈接
-c 編譯到目標(biāo)代碼
-o 文件輸出到 文件
-static 此選項(xiàng)對生成的文件采用靜態(tài)鏈接
-g 生成調(diào)試信息。GNU 調(diào)試器可利用該信息
-shared 此選項(xiàng)將盡量使用動(dòng)態(tài)庫,所以生成文件比較小,但是需要系統(tǒng)由動(dòng)態(tài)庫
-O0
-O1
-O2
-O3 編譯器的優(yōu)化選項(xiàng)的4個(gè)級別,-O0表示沒有優(yōu)化,-O1為缺省值,-O3優(yōu)化級別最高
-w 不生成任何警告信息
-Wall 生成所有警告信息(默認(rèn)生成)
1.預(yù)處理(Preprocessing)

由編譯器完成,將所有的#include頭文件以及宏定義替換成其真正的內(nèi)容
gcc的預(yù)處理是預(yù)處理器cpp來完成的

我們可以用以下指令對.c文件進(jìn)行預(yù)處理:

gcc -E a.c -o a.i

或者

cpp a.c -o a.i

-o:代表輸出到指定文件

在這里插入圖片描述
可以看到文件預(yù)處理后變大了很多
打開a.i文件發(fā)現(xiàn)是把我們用的頭文件stdio.h所涉及的其他頭文件以及宏定義,變量都引入了進(jìn)來
在這里插入圖片描述

在這里插入圖片描述
可以看到文件最后才是我們寫的c代碼,實(shí)際上我們能直接使用printf等函數(shù)是因?yàn)槲覀円昧艘粋€(gè)頭文件,使得我們可以少寫很多代碼,就相當(dāng)于自己事先把一個(gè)自定義的函數(shù)寫到一個(gè)文件里,下次再使用直接引用這個(gè)文件就可以了,而不用自己再把以前定義好的函數(shù)再抄一遍了,我們用的gcc編譯器的預(yù)處理就幫助我們省略了抄定義好的函數(shù),變量等東西這一步

2.編譯(Compilation)

由編譯器完成,將經(jīng)過預(yù)處理之后的程序轉(zhuǎn)換成特定匯編代碼的過程, 編譯的命令如下:

gcc -S a.i -o a.s

在這里插入圖片描述
執(zhí)行這一步程序會(huì)出現(xiàn)一個(gè)warning,警告:函數(shù)gets的隱式聲明;你是說“fgets”嗎?
為什么會(huì)出現(xiàn)這個(gè)呢,這是因?yàn)間cc編譯器太強(qiáng)了,檢測出我們使用了gets函數(shù),然后說gets函數(shù)很危險(xiǎn),建議用fgets函數(shù),這個(gè)問題在后面會(huì)講

我們先來查看一下編譯好的匯編代碼

.file	"a.c"
	.text
	.section	.rodata
.LC0:
	.string	"Please input your name:"
	.text
	.globl	main
	.type	main, @function
main:
.LFB0:
	.cfi_startproc
	endbr64
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	subq	$32, %rsp
	movq	%fs:40, %rax
	movq	%rax, -8(%rbp)
	xorl	%eax, %eax
	movq	$0, -32(%rbp)
	movq	$0, -24(%rbp)
	leaq	.LC0(%rip), %rax
	movq	%rax, %rdi
	movl	$0, %eax
	call	printf@PLT
	leaq	-32(%rbp), %rax
	movq	%rax, %rdi
	movl	$0, %eax
	call	gets@PLT
	leaq	-32(%rbp), %rax
	movq	%rax, %rdi
	call	hello
	movl	$0, %eax
	movq	-8(%rbp), %rdx
	subq	%fs:40, %rdx
	je	.L3
	call	__stack_chk_fail@PLT
.L3:
	leave
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE0:
	.size	main, .-main
	.section	.rodata
.LC1:
	.string	"hello,%s\n"
	.text
	.globl	hello
	.type	hello, @function
hello:
.LFB1:
	.cfi_startproc
	endbr64
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	subq	$16, %rsp
	movq	%rdi, -8(%rbp)
	movq	-8(%rbp), %rax
	movq	%rax, %rsi
	leaq	.LC1(%rip), %rax
	movq	%rax, %rdi
	movl	$0, %eax
	call	printf@PLT
	nop
	leave
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE1:
	.size	hello, .-hello
	.ident	"GCC: (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0"
	.section	.note.GNU-stack,"",@progbits
	.section	.note.gnu.property,"a"
	.align 8
	.long	1f - 0f
	.long	4f - 1f
	.long	5
0:
	.string	"GNU"
1:
	.align 8
	.long	0xc0000002
	.long	3f - 2f
2:
	.long	0x3
3:
	.align 8
4:

這是AT&T匯編語言,初學(xué)者可能看上去很復(fù)雜,暫且不看
接著往下走

3.匯編(Assemble)

由匯編器完成,匯編過程將上一步的匯編代碼轉(zhuǎn)換成機(jī)器碼(machine code),這一步產(chǎn)生了二進(jìn)制的目標(biāo)文件(.o文件)(ELF文件), gcc匯編過程通過as命令完成

gcc -c a.s -o a.o

或者

as a.s -o a.o

查看一下a.o
在這里插入圖片描述
這已經(jīng)把我們的匯編語言變成二進(jìn)制目標(biāo)代碼(.o文件)了
.o文件又稱對象文件,是可執(zhí)行文件,是可重定向文件的一種,通常以ELF格式保存,里面包含了對各個(gè)函數(shù)的入口標(biāo)記,描述,當(dāng)程序要執(zhí)行時(shí)還需要鏈接(link).鏈接就是把多個(gè).o文件鏈成一個(gè)可執(zhí)行文件。
中間插一段elf文件的內(nèi)容

番外篇1–ELF文件

ELF文件格式是linux下可執(zhí)行文件的一種格式

在這里插入圖片描述

幾種類型的ELF文件:
  • 可重定位文件(Relocatable File):用戶和其他目標(biāo)文件一起創(chuàng)建可執(zhí)行文件或者共享目標(biāo)文件,例如lib*.a文件。
  • 可執(zhí)行文件(Executable File):用于生成進(jìn)程映像,載入內(nèi)存執(zhí)行,例如編譯好的可執(zhí)行文件a.out。
  • 共享目標(biāo)文件(Shared Object File):用于和其他共享目標(biāo)文件或者可重定位文件一起生成elf目標(biāo)文件或者和執(zhí)行文件一起創(chuàng)建進(jìn)程映像,例如lib*.so文件。
  • 核心轉(zhuǎn)儲(chǔ)文件(Core Dump File),當(dāng)進(jìn)程意外終止時(shí),系統(tǒng)可以將該進(jìn)程的地址空間內(nèi)容及終止時(shí)的一些其他信息轉(zhuǎn)儲(chǔ)到核心轉(zhuǎn)儲(chǔ)文件,如linux下的coredump文件。
ELF文件作用:

ELF文件參與程序的連接(建立一個(gè)程序)和程序的執(zhí)行(運(yùn)行一個(gè)程序),所以可以從不同的角度來看待elf格式的文件:

  • 如果用于編譯和鏈接(可重定位文件),則編譯器和鏈接器將把elf文件看作是節(jié)頭表描述的節(jié)的集合,程序頭表可選。
  • 如果用于加載執(zhí)行(可執(zhí)行文件),則加載器則將把elf文件看作是程序頭表描述的段的集合,一個(gè)段可能包含多個(gè)節(jié),節(jié)頭表可選。
  • 如果是共享文件,則兩者都含有。
ELF文件總體組成:

elf文件頭描述elf文件的總體信息。包括:系統(tǒng)相關(guān),類型相關(guān),加載相關(guān),鏈接相關(guān)。

  • 系統(tǒng)相關(guān)表示:elf文件標(biāo)識的魔術(shù)數(shù),以及硬件和平臺(tái)等相關(guān)信息,增加了elf文件的移植性,使交叉編譯成為可能。
  • 類型相關(guān)就是前面說的那個(gè)類型。
  • 加載相關(guān):包括程序頭表相關(guān)信息。
  • 鏈接相關(guān):節(jié)頭表相關(guān)信息。

可以用readelf命令來查看elf文件信息
如查看elf文件頭信息

readelf -h a.o

在這里插入圖片描述

readelf命令解析

-a 
--all 顯示全部信息,等價(jià)于 -h -l -S -s -r -d -V -A -I. 

-h 
--file-header 顯示elf文件開始的文件頭信息. 

-l 
--program-headers  
--segments 顯示程序頭(段頭)信息(如果有的話)。 

-S 
--section-headers  
--sections 顯示節(jié)頭信息(如果有的話)。 

-g 
--section-groups 顯示節(jié)組信息(如果有的話)。 

-t 
--section-details 顯示節(jié)的詳細(xì)信息(-S的)。 

-s 
--syms        
--symbols 顯示符號表段中的項(xiàng)(如果有的話)。 

-e 
--headers 顯示全部頭信息,等價(jià)于: -h -l -S 

-n 
--notes 顯示note段(內(nèi)核注釋)的信息。 

-r 
--relocs 顯示可重定位段的信息。 

-u 
--unwind 顯示unwind段信息。當(dāng)前只支持IA64 ELF的unwind段信息。 

-d 
--dynamic 顯示動(dòng)態(tài)段的信息。 

-V 
--version-info 顯示版本段的信息。 

-A 
--arch-specific 顯示CPU構(gòu)架信息。 

-D 
--use-dynamic 使用動(dòng)態(tài)段中的符號表顯示符號,而不是使用符號段。 

-x--hex-dump=以16進(jìn)制方式顯示指定段內(nèi)內(nèi)容。number指定段表中段的索引,或字符串指定文件中的段名。 

-w[liaprmfFsoR] or 
--debug-dump[=line,=info,=abbrev,=pubnames,=aranges,=macro,=frames,=frames-interp,=str,=loc,=Ranges] 顯示調(diào)試段中指定的內(nèi)容。 

-I 
--histogram 顯示符號的時(shí)候,顯示bucket list長度的柱狀圖。 

-v 
--version 顯示readelf的版本信息。 

-H 
--help 顯示readelf所支持的命令行選項(xiàng)。 

-W 
--wide 寬行輸出。

elf文件格式先講這些,接著往下看

4.鏈接(Linking)

鏈接過程實(shí)際上是把多個(gè)可重定位文件合并成一個(gè)可執(zhí)行文件的過程。這個(gè)過程中最重要的兩個(gè)步驟是符號解析和重定位。所謂的符號解析,就是將符號的定義和引用關(guān)聯(lián)起來,而重定位就是給所有符號和指令添加運(yùn)行時(shí)的地址的過程。

過程

  • 確定符號引用關(guān)系(符號解析)
  • 合并相關(guān) .o 文件(重定位)
  • 確定每個(gè)符號的地址(重定位)
  • 在指令中填入新的地址(重定位)

我們的程序最終是要裝載到運(yùn)行內(nèi)存中才可以執(zhí)行的,所以需要規(guī)定一種文件格式來確定裝載到內(nèi)存后對應(yīng)的地址

由鏈接器完成,鏈接分為兩種:動(dòng)態(tài)鏈接和靜態(tài)鏈接
GCC默認(rèn)情況下以動(dòng)態(tài)庫方式link

靜態(tài)鏈接

命令

gcc -static a.o -o a

實(shí)際是gcc調(diào)用了ld鏈接器,因?yàn)槌绦蛐枰溄雍芏嘞到y(tǒng)文件,所以這里不推薦使用ld命令鏈接
在這里插入圖片描述

這里出現(xiàn)gets函數(shù)比較危險(xiǎn),跟編譯成匯編時(shí)一樣的意思,先不管這里,后面再講

對于靜態(tài)鏈接而言,程序鏈接后的地址其實(shí)就是在裝載進(jìn)內(nèi)存后,程序運(yùn)行的地址

在這里插入圖片描述
在這里插入圖片描述
可以看到符號表已經(jīng)有了地址,符號表在鏈接時(shí)已經(jīng)重定位,當(dāng)程序被裝載進(jìn)內(nèi)存時(shí),運(yùn)行時(shí)所用的虛擬地址就是這個(gè)地址

動(dòng)態(tài)鏈接

GCC默認(rèn)情況下以動(dòng)態(tài)庫方式link

gcc a.o -o a

在這里插入圖片描述
用readelf命令查看一下生成的ELF文件的信息
在這里插入圖片描述
在這里插入圖片描述
這里符號表的地址只是偏移地址(相對于ELF文件首地址的偏移),而ELF文件的首地址在程序裝載進(jìn)內(nèi)存時(shí)才會(huì)分配,而動(dòng)態(tài)符號表(.dynsym)的地址也沒有分配,只有在程序運(yùn)行時(shí)才會(huì)分配地址,這是動(dòng)態(tài)鏈接的一個(gè)延遲綁定機(jī)制
查看程序節(jié)頭信息會(huì)發(fā)現(xiàn)動(dòng)態(tài)鏈接比靜態(tài)鏈接多出幾個(gè)符號表

readelf -S a
  • 動(dòng)態(tài)鏈接

    There are 31 section headers, starting at offset 0x3710:

    節(jié)頭:
    [號] 名稱 類型 地址 偏移量
    大小 全體大小 旗標(biāo) 鏈接 信息 對齊
    [ 0] NULL 0000000000000000 00000000
    0000000000000000 0000000000000000 0 0 0
    [ 1] .interp PROGBITS 0000000000000318 00000318
    000000000000001c 0000000000000000 A 0 0 1
    [ 2] .note.gnu.pr[…] NOTE 0000000000000338 00000338
    0000000000000030 0000000000000000 A 0 0 8
    [ 3] .note.gnu.bu[…] NOTE 0000000000000368 00000368
    0000000000000024 0000000000000000 A 0 0 4
    [ 4] .note.ABI-tag NOTE 000000000000038c 0000038c
    0000000000000020 0000000000000000 A 0 0 4
    [ 5] .gnu.hash GNU_HASH 00000000000003b0 000003b0
    0000000000000024 0000000000000000 A 6 0 8
    [ 6] .dynsym DYNSYM 00000000000003d8 000003d8
    00000000000000d8 0000000000000018 A 7 1 8
    [ 7] .dynstr STRTAB 00000000000004b0 000004b0
    00000000000000af 0000000000000000 A 0 0 1
    [ 8] .gnu.version VERSYM 0000000000000560 00000560
    0000000000000012 0000000000000002 A 6 0 2
    [ 9] .gnu.version_r VERNEED 0000000000000578 00000578
    0000000000000040 0000000000000000 A 7 1 8
    [10] .rela.dyn RELA 00000000000005b8 000005b8
    00000000000000c0 0000000000000018 A 6 0 8
    [11] .rela.plt RELA 0000000000000678 00000678
    0000000000000048 0000000000000018 AI 6 24 8
    [12] .init PROGBITS 0000000000001000 00001000
    000000000000001b 0000000000000000 AX 0 0 4
    [13] .plt PROGBITS 0000000000001020 00001020
    0000000000000040 0000000000000010 AX 0 0 16
    [14] .plt.got PROGBITS 0000000000001060 00001060
    0000000000000010 0000000000000010 AX 0 0 16
    [15] .plt.sec PROGBITS 0000000000001070 00001070
    0000000000000030 0000000000000010 AX 0 0 16
    [16] .text PROGBITS 00000000000010a0 000010a0
    000000000000018e 0000000000000000 AX 0 0 16
    [17] .fini PROGBITS 0000000000001230 00001230
    000000000000000d 0000000000000000 AX 0 0 4
    [18] .rodata PROGBITS 0000000000002000 00002000
    0000000000000026 0000000000000000 A 0 0 4
    [19] .eh_frame_hdr PROGBITS 0000000000002028 00002028
    000000000000003c 0000000000000000 A 0 0 4
    [20] .eh_frame PROGBITS 0000000000002068 00002068
    00000000000000cc 0000000000000000 A 0 0 8
    [21] .init_array INIT_ARRAY 0000000000003da8 00002da8
    0000000000000008 0000000000000008 WA 0 0 8
    [22] .fini_array FINI_ARRAY 0000000000003db0 00002db0
    0000000000000008 0000000000000008 WA 0 0 8
    [23] .dynamic DYNAMIC 0000000000003db8 00002db8
    00000000000001f0 0000000000000010 WA 7 0 8
    [24] .got PROGBITS 0000000000003fa8 00002fa8
    0000000000000058 0000000000000008 WA 0 0 8
    [25] .data PROGBITS 0000000000004000 00003000
    0000000000000010 0000000000000000 WA 0 0 8
    [26] .bss NOBITS 0000000000004010 00003010
    0000000000000008 0000000000000000 WA 0 0 1
    [27] .comment PROGBITS 0000000000000000 00003010
    000000000000002b 0000000000000001 MS 0 0 1
    [28] .symtab SYMTAB 0000000000000000 00003040
    00000000000003a8 0000000000000018 29 18 8
    [29] .strtab STRTAB 0000000000000000 000033e8
    000000000000020b 0000000000000000 0 0 1
    [30] .shstrtab STRTAB 0000000000000000 000035f3
    000000000000011a 0000000000000000 0 0 1
    Key to Flags:
    W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
    L (link order), O (extra OS processing required), G (group), T (TLS),
    C (compressed), x (unknown), o (OS specific), E (exclude),
    D (mbind), l (large), p (processor specific)

  • 靜態(tài)鏈接
    There are 32 section headers, starting at offset 0xdb548:
    節(jié)頭:
    [號] 名稱 類型 地址 偏移量
    大小 全體大小 旗標(biāo) 鏈接 信息 對齊
    [ 0] NULL 0000000000000000 00000000
    0000000000000000 0000000000000000 0 0 0
    [ 1] .note.gnu.pr[…] NOTE 0000000000400270 00000270
    0000000000000030 0000000000000000 A 0 0 8
    [ 2] .note.gnu.bu[…] NOTE 00000000004002a0 000002a0
    0000000000000024 0000000000000000 A 0 0 4
    [ 3] .note.ABI-tag NOTE 00000000004002c4 000002c4
    0000000000000020 0000000000000000 A 0 0 4
    [ 4] .rela.plt RELA 00000000004002e8 000002e8
    0000000000000240 0000000000000018 AI 29 20 8
    [ 5] .init PROGBITS 0000000000401000 00001000
    000000000000001b 0000000000000000 AX 0 0 4
    [ 6] .plt PROGBITS 0000000000401020 00001020
    0000000000000180 0000000000000000 AX 0 0 16
    [ 7] .text PROGBITS 00000000004011c0 000011c0
    0000000000095138 0000000000000000 AX 0 0 64
    [ 8] __libc_freeres_fn PROGBITS 0000000000496300 00096300
    00000000000014cd 0000000000000000 AX 0 0 16
    [ 9] .fini PROGBITS 00000000004977d0 000977d0
    000000000000000d 0000000000000000 AX 0 0 4
    [10] .rodata PROGBITS 0000000000498000 00098000
    000000000001cb2c 0000000000000000 A 0 0 32
    [11] .stapsdt.base PROGBITS 00000000004b4b2c 000b4b2c
    0000000000000001 0000000000000000 A 0 0 1
    [12] .eh_frame PROGBITS 00000000004b4b30 000b4b30
    000000000000b928 0000000000000000 A 0 0 8
    [13] .gcc_except_table PROGBITS 00000000004c0458 000c0458
    0000000000000122 0000000000000000 A 0 0 1
    [14] .tdata PROGBITS 00000000004c17b0 000c07b0
    0000000000000020 0000000000000000 WAT 0 0 8
    [15] .tbss NOBITS 00000000004c17d0 000c07d0
    0000000000000048 0000000000000000 WAT 0 0 8
    [16] .init_array INIT_ARRAY 00000000004c17d0 000c07d0
    0000000000000008 0000000000000008 WA 0 0 8
    [17] .fini_array FINI_ARRAY 00000000004c17d8 000c07d8
    0000000000000008 0000000000000008 WA 0 0 8
    [18] .data.rel.ro PROGBITS 00000000004c17e0 000c07e0
    0000000000003788 0000000000000000 WA 0 0 32
    [19] .got PROGBITS 00000000004c4f68 000c3f68
    0000000000000098 0000000000000000 WA 0 0 8
    [20] .got.plt PROGBITS 00000000004c5000 000c4000
    00000000000000d8 0000000000000008 WA 0 0 8
    [21] .data PROGBITS 00000000004c50e0 000c40e0
    00000000000019e0 0000000000000000 WA 0 0 32
    [22] __libc_subfreeres PROGBITS 00000000004c6ac0 000c5ac0
    0000000000000048 0000000000000000 WAR 0 0 8
    [23] __libc_IO_vtables PROGBITS 00000000004c6b20 000c5b20
    0000000000000768 0000000000000000 WA 0 0 32
    [24] __libc_atexit PROGBITS 00000000004c7288 000c6288
    0000000000000008 0000000000000000 WAR 0 0 8
    [25] .bss NOBITS 00000000004c72a0 000c6290
    0000000000005980 0000000000000000 WA 0 0 32
    [26] __libc_freer[…] NOBITS 00000000004ccc20 000c6290
    0000000000000020 0000000000000000 WA 0 0 8
    [27] .comment PROGBITS 0000000000000000 000c6290
    000000000000002b 0000000000000001 MS 0 0 1
    [28] .note.stapsdt NOTE 0000000000000000 000c62bc
    0000000000001648 0000000000000000 0 0 4
    [29] .symtab SYMTAB 0000000000000000 000c7908
    000000000000c480 0000000000000018 30 770 8
    [30] .strtab STRTAB 0000000000000000 000d3d88
    0000000000007668 0000000000000000 0 0 1
    [31] .shstrtab STRTAB 0000000000000000 000db3f0
    0000000000000157 0000000000000000 0 0 1

這是因?yàn)樗鼈兊闹囟ㄏ驒C(jī)制不同
先來說一下兩個(gè)符號表

  • 1.全局偏移表(GOT):存放外部函數(shù)地址的數(shù)據(jù)段。

  • 2.程序連接表(PLT):用來獲取數(shù)據(jù)段記錄的外部函數(shù)地址的代碼。

    print_banner: printf@plt: printf@got: 0xf7e835f0 :
    … jmp *printf@got 0xf7e835f0 …
    call printf@plt ret
    … …

    可執(zhí)行文件 PLT表 GOT表 glibc中的printf函數(shù)

    GOT是一個(gè)存儲(chǔ)外部庫函數(shù)的表

    PLT則是由代碼片段組成的,每個(gè)代碼片段都跳轉(zhuǎn)到GOT表中的一個(gè)具體的函數(shù)調(diào)用

這里講一下重定位

番外篇2–重定位 鏈接時(shí)重定位–靜態(tài)鏈接

鏈接階段是將一個(gè)或多個(gè)中間文件(.o文件)通過鏈接器將它們鏈接成一個(gè)可執(zhí)行文件,主要做的事情有

對各個(gè)中間文件的同名section進(jìn)行合并

對代碼段,數(shù)據(jù)段等進(jìn)行地址分配

進(jìn)行鏈接時(shí)重定位

兩種情況:

如果是在其他中間文件中已經(jīng)定義了的函數(shù),鏈接階段可以直接重定位到函數(shù)地址

如果是在動(dòng)態(tài)庫中定義了的函數(shù),鏈接階段無法直接重定位到函數(shù)地址,只能生成額外的小片段代碼,也就是PLT表,然后重定位到該代碼片段
運(yùn)行時(shí)重定位–動(dòng)態(tài)鏈接

運(yùn)行后加載動(dòng)態(tài)庫,把動(dòng)態(tài)庫中的相應(yīng)函數(shù)地址填入GOT表,由于PLT表是跳轉(zhuǎn)到GOT表的,這就構(gòu)成了運(yùn)行時(shí)重定位

延遲重定位–動(dòng)態(tài)鏈接

只有動(dòng)態(tài)庫函數(shù)在被調(diào)用時(shí),才會(huì)進(jìn)行地址解析和重定位工作,這時(shí)候動(dòng)態(tài)庫函數(shù)的地址才會(huì)被寫入到GOT表項(xiàng)中

請?zhí)砑訄D片描述
第一步由函數(shù)調(diào)用跳入到PLT表中,然后第二步PLT表跳到GOT表中,可以看到第三步由GOT表回跳到PLT表中,這時(shí)候進(jìn)行壓棧,把代表函數(shù)的ID壓棧,接著第四步跳轉(zhuǎn)到公共的PLT表項(xiàng)中,第5步進(jìn)入到GOT表中,然后_dl_runtime_resolve對動(dòng)態(tài)函數(shù)進(jìn)行地址解析和重定位,第七步把動(dòng)態(tài)函數(shù)真實(shí)的地址寫入到GOT表項(xiàng)中,然后執(zhí)行函數(shù)并返回。

解釋下dynamic段,link_map和_dl_runtime_resolve

dynamic段:提供動(dòng)態(tài)鏈接的信息,例如動(dòng)態(tài)鏈接中各個(gè)表的位置
link_map:已加載庫的鏈表,由動(dòng)態(tài)庫函數(shù)的地址構(gòu)成的鏈表
_dl_runtime_resolve:在第一次運(yùn)行時(shí)進(jìn)行地址解析和重定位工作

請?zhí)砑訄D片描述

可以看到,第一步還是由函數(shù)調(diào)用跳入到PLT表,但是第二步跳入到GOT表中時(shí),由于這個(gè)時(shí)候該表項(xiàng)已經(jīng)是動(dòng)態(tài)函數(shù)的真實(shí)地址了,所以可以直接執(zhí)行然后返回。

對于動(dòng)態(tài)函數(shù)的調(diào)用,第一次要經(jīng)過地址解析和回寫到GOT表項(xiàng)中,第二次直接調(diào)用即可

運(yùn)行可執(zhí)行文件 裝載進(jìn)內(nèi)存

程序編譯鏈接完成后是保存在硬盤中的,當(dāng)用戶執(zhí)行該程序的時(shí)候,該程序(ELF可執(zhí)行文件)會(huì)被加載器按照program header table(可執(zhí)行文件頭)的描述將程序的代碼段和數(shù)據(jù)段從硬盤加載到內(nèi)存中。
在這里插入圖片描述
如果是靜態(tài)鏈接就是直接將各個(gè)段的地址寫入內(nèi)存,如果是動(dòng)態(tài)鏈接就先隨機(jī)分配一個(gè)地址給ELF文件頭,作為首地址,然后ELF文件其他數(shù)據(jù)根據(jù)偏移量確定地址。
這里的地址都是虛擬地址,在運(yùn)行內(nèi)存中有著許多進(jìn)程,每個(gè)進(jìn)程又包含至少一個(gè)線程
進(jìn)程
這里講一下進(jìn)程與線程

番外篇3–進(jìn)程與線程

gcc編譯好的ELF可執(zhí)行文件叫程序
那么運(yùn)行時(shí)的程序就叫做進(jìn)程,進(jìn)程之間通過 TCP/IP 端口實(shí)現(xiàn)交互

進(jìn)程是申請一塊內(nèi)存空間,將數(shù)據(jù)放到內(nèi)存空間中去, 是申請數(shù)據(jù)的過程
是最小的資源管理單元

而線程就是進(jìn)程的子集,多個(gè)線程共享同一塊內(nèi)存(由進(jìn)程向操作系統(tǒng)申請),通過共享的內(nèi)存空間來進(jìn)行交互

是進(jìn)程的一條流水線, 只用來執(zhí)行程序,而不涉及到申請資源, 是程序的實(shí)際執(zhí)行者
最小的執(zhí)行單元

在這里插入圖片描述

總的來說,程序要運(yùn)行,需要先向操作系統(tǒng)申請一段內(nèi)存作為進(jìn)程

進(jìn)程空間的內(nèi)存分配

在這里插入圖片描述
對于linux來說,從 Linux 內(nèi)核的角度來看,進(jìn)程和線程都是一樣的。
系統(tǒng)調(diào)用fork()可以新建一個(gè)子進(jìn)程,函數(shù)pthread()可以新建一個(gè)線程。但無論線程還是進(jìn)程,都是用task_struct結(jié)構(gòu)表示的,,唯一的區(qū)別就是共享的數(shù)據(jù)區(qū)域不同。
Linux 系統(tǒng)將線程看做共享數(shù)據(jù)的進(jìn)程

番外篇4–函數(shù)調(diào)用棧(線程棧)

函數(shù)調(diào)用經(jīng)常是嵌套的,在同一時(shí)刻,堆棧中會(huì)有多個(gè)函數(shù)的信息。每個(gè)未完成運(yùn)行的函數(shù)占用一個(gè)獨(dú)立的連續(xù)區(qū)域,稱作棧幀(Stack Frame)。棧幀是堆棧的邏輯片段,當(dāng)調(diào)用函數(shù)時(shí)邏輯棧幀被壓入堆棧, 當(dāng)函數(shù)返回時(shí)邏輯棧幀被從堆棧中彈出。棧幀存放著函數(shù)參數(shù),局部變量及恢復(fù)前一棧幀所需要的數(shù)據(jù)等。
棧幀的邊界由棧幀基地址指針EBP和堆棧指針ESP界定(指針存放在相應(yīng)寄存器中)。EBP指向當(dāng)前棧幀底部(高地址),在當(dāng)前棧幀內(nèi)位置固定;ESP指向當(dāng)前棧幀頂部(低地址),當(dāng)程序執(zhí)行時(shí)ESP會(huì)隨著數(shù)據(jù)的入棧和出棧而移動(dòng)。因此函數(shù)中對大部分?jǐn)?shù)據(jù)的訪問都基于EBP進(jìn)行。

函數(shù)調(diào)用時(shí)入棧順序?yàn)?/p>

實(shí)參N~1 → 主調(diào)函數(shù)返回地址→主調(diào)函數(shù)幀基指針EBP → 被調(diào)函數(shù)局部變量1~N

請?zhí)砑訄D片描述
對于我們剛寫的的hello.c來說,大概是這樣的

在這里插入圖片描述

CPU開始運(yùn)行程序

上面說進(jìn)程向系統(tǒng)申請了一段內(nèi)存,gcc編譯生成的ELF可執(zhí)行文件會(huì)將自己數(shù)據(jù)映射到這段內(nèi)存上,而線程作為進(jìn)程的執(zhí)行單位擁有自己的堆棧,棧上有一個(gè)個(gè)棧幀來臨時(shí)存儲(chǔ)數(shù)據(jù)。
CPU運(yùn)行程序的過程就是執(zhí)行一條條指令的過程,這些指令就是gcc編譯鏈接成的ELF可執(zhí)行文件里的指令(也就是.text段里的代碼段的機(jī)器代碼)。

  • cpu的程序寄存器存放著要執(zhí)行的下一條指令的地址,通過這個(gè)地址去訪問下一條指令,
  • 指令寄存器讀取下一條指令,
  • 邏輯運(yùn)算單元通過分析各個(gè)寄存器中的數(shù)據(jù)去執(zhí)行指令,
  • 之后程序寄存器中的值進(jìn)行自加,執(zhí)行下一指令的地址,如此循環(huán)下去,執(zhí)行完整個(gè)程序。

棧幀的創(chuàng)建和銷毀也是通過CPU執(zhí)行ELF文件里的代碼段中的指令實(shí)現(xiàn)的。

請?zhí)砑訄D片描述

番外篇5–虛擬內(nèi)存與內(nèi)存尋址
  • 操作系統(tǒng)會(huì)提供一種機(jī)制,將不同進(jìn)程的虛擬地址和不同內(nèi)存的物理地址映射起來。我們上面看到的地址其實(shí)都是虛擬地址,這一段虛擬地址對應(yīng)的物理地址可能是不連續(xù)的。
  • CPU訪問內(nèi)存時(shí),進(jìn)程持有的虛擬地址會(huì)通過 CPU 芯片中的內(nèi)存管理單元(MMU)的映射關(guān)系,來轉(zhuǎn)換變成物理地址,然后再通過物理地址訪問內(nèi)存。
  • MMU的轉(zhuǎn)換分兩個(gè)階段,分段機(jī)制和分頁機(jī)制,分段把虛擬地址轉(zhuǎn)換為線性地址,分頁把線性地址轉(zhuǎn)換為物理地址。
結(jié)語

至此,c程序從編譯開始到運(yùn)行結(jié)束的過程就結(jié)束了。
到這里就可以解釋gcc進(jìn)行第二步操作編譯時(shí)和靜態(tài)鏈接時(shí)的警告了。
我們再看一下我們的a.c文件

#includevoid hello(char * name);
int main()
{char name[16]={0};
	printf("Please input your name:");
	gets(name);
	hello(name);
	return 0;
}
void hello(char * name)
{printf("hello,%s\n",name);
}

我們的name數(shù)組只有16字節(jié)大小,gets函數(shù)可以輸入超過16字節(jié)的數(shù)據(jù),根據(jù)函數(shù)調(diào)用棧的原理,name數(shù)組存放的位置與main返回地址是挨著的,如果輸入過長的數(shù)據(jù)可能會(huì)破壞返回地址的數(shù)據(jù),致使程序返回到一個(gè)錯(cuò)誤的地址,如果這個(gè)地址無意義,程序不能正常退出,就會(huì)報(bào)錯(cuò),如果這個(gè)地址被我們設(shè)置成一個(gè)特定的有惡意代碼的地址,程序就會(huì)去執(zhí)行我們的惡意代碼,造成危險(xiǎn)。所以gcc向我們發(fā)送警告,讓我們使用fgets函數(shù)來限制輸入字節(jié)為16字節(jié)以內(nèi)。

你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級服務(wù)器適合批量采購,新人活動(dòng)首月15元起,快前往官網(wǎng)查看詳情吧

網(wǎng)站題目:CTF--PWN必備技能--理解c程序從編譯開始到運(yùn)行結(jié)束的過程-創(chuàng)新互聯(lián)
文章轉(zhuǎn)載:http://jinyejixie.com/article46/egieg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供關(guān)鍵詞優(yōu)化定制網(wǎng)站、營銷型網(wǎng)站建設(shè)面包屑導(dǎo)航、網(wǎng)站設(shè)計(jì)公司、動(dòng)態(tài)網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

微信小程序開發(fā)
邮箱| 高邑县| 金华市| 彝良县| 泗洪县| 十堰市| 滨海县| 五河县| 霍林郭勒市| 富裕县| 秀山| 海口市| 海口市| 九台市| 噶尔县| 茌平县| 九龙城区| 大悟县| 博湖县| 从化市| 同德县| 报价| 如皋市| 上饶县| 永新县| 连州市| 长顺县| 称多县| 江口县| 三穗县| 凯里市| 大冶市| 承德市| 阿坝县| 驻马店市| 沅陵县| 信丰县| 大田县| 临安市| 玛纳斯县| 伊通|