新聞中心
棧(Stack)是計算機內(nèi)存中的一種數(shù)據(jù)結(jié)構(gòu),用于保存函數(shù)調(diào)用中的參數(shù)和局部變量等信息。但是,棧在實現(xiàn)過程中存在缺陷,即棧溢出(Stack Overflow),也就是在棧空間中寫入超過其分配空間的數(shù)據(jù)。攻擊者可以利用這個漏洞實現(xiàn)棧溢出攻擊。而Linux系統(tǒng)是這類棧溢出攻擊的主要受害者,因此本文將深入探討Linux經(jīng)典棧溢出攻擊的實現(xiàn)原理和防范方法。

在成都網(wǎng)站設(shè)計、成都做網(wǎng)站過程中,需要針對客戶的行業(yè)特點、產(chǎn)品特性、目標受眾和市場情況進行定位分析,以確定網(wǎng)站的風格、色彩、版式、交互等方面的設(shè)計方向。創(chuàng)新互聯(lián)公司還需要根據(jù)客戶的需求進行功能模塊的開發(fā)和設(shè)計,包括內(nèi)容管理、前臺展示、用戶權(quán)限管理、數(shù)據(jù)統(tǒng)計和安全保護等功能。
一、棧溢出攻擊原理
我們需要了解一些基礎(chǔ)概念。
1.1 調(diào)用棧
當一個函數(shù)被調(diào)用時,它的程序參數(shù)存儲在棧中的一部分內(nèi)存中,同時在棧的頂部存儲了該函數(shù)的返回地址,也就是說當該函數(shù)執(zhí)行完畢之后會返回到該地址處繼續(xù)執(zhí)行。調(diào)用棧是這樣的一個數(shù)據(jù)結(jié)構(gòu),調(diào)用函數(shù)時使用,當函數(shù)返回時撤銷之前的函數(shù)調(diào)用。
1.2 棧溢出
當向一個緩沖區(qū)寫入超過其本身容量的數(shù)據(jù)時,數(shù)據(jù)就會覆蓋那個緩沖區(qū)后面的內(nèi)存地址。如果這個被寫入的位置正好是調(diào)用棧元素的位置,那么就會覆蓋這個調(diào)用棧上的棧幀(Stack Frame)中的數(shù)據(jù),同時也會覆蓋存儲在棧中的返回地址。由此,攻擊者就可以在函數(shù)返回時跳轉(zhuǎn)到自己編寫的代碼中去執(zhí)行,從而完全控制程序的執(zhí)行流程。
一旦攻擊者控制了程序的執(zhí)行流程,他就可以做任何事情,例如啟動后門程序、讀取機密文件、改變環(huán)境變量、執(zhí)行任意代碼等等。由于棧溢出是一種常見的漏洞類型,惡意攻擊者利用棧溢出攻擊常常能夠成功。
1.3 棧溢出攻擊的實現(xiàn)方式
基本原理已經(jīng)解釋清楚了,攻擊者實現(xiàn)這么一個棧溢出攻擊的方法通常有兩種:
1.3.1 覆蓋函數(shù)返回地址
攻擊者通過覆蓋棧上的返回地址,使得當前函數(shù)執(zhí)行完成時會跳轉(zhuǎn)到攻擊者編寫的代碼處繼續(xù)執(zhí)行。這個方法實現(xiàn)起來比較簡單,只要知道棧幀的大小,計算出攻擊者的代碼地址就可以,因為跳轉(zhuǎn)地址就是覆蓋掉的返回地址。
1.3.2 利用shellcode
Shellcode可以看作是一段正常的程序,但是它的目標是執(zhí)行攻擊者想要的命令。攻擊者可以在棧溢出時把shellcode放入到棧緩沖區(qū)中,然后同時覆蓋掉返回地址,這樣函數(shù)執(zhí)行完畢時就會跳轉(zhuǎn)到這個shellcode執(zhí)行攻擊者編寫的程序,這種方式在繞過程序棧非執(zhí)行區(qū)域的保護措施上比較有用。
二、Linux下的棧溢出攻擊
Linux系統(tǒng)是一種受到極大威脅的操作系統(tǒng)。因此,對于Linux下的棧溢出攻擊,我們需要重點關(guān)注以下幾點。
2.1 編譯器優(yōu)化引起的問題
Ubuntu發(fā)行版下,gcc 5 開始默認啟用了棧保護技術(shù),即在棧上加入了一段特定的隨機值作為stack canary,用于在函數(shù)返回時防止棧被破壞,這樣就可以一定程度上防止棧溢出攻擊。當棧被修改時,會觸發(fā) stack canary 檢查,導致程序異常終止。
但是,在編寫某些特定類型程序時,利用未經(jīng)驗證的用戶輸入,或者字符串格式化等可疑操作時,這種技術(shù)可能未能及時發(fā)現(xiàn)棧溢出攻擊,同時這種技術(shù)對程序帶來的額外負荷也是不能忽略的。
2.2 代碼注入
代碼注入通常是通過覆蓋函數(shù)棧幀返回地址的方法實現(xiàn)的。在覆蓋函數(shù)返回地址時,攻擊者可以將指向攻擊者編寫的惡意代碼的地址寫入該位置。當緩沖區(qū)溢出時,程序就會跳轉(zhuǎn)到惡意代碼,并執(zhí)行該代碼。這種攻擊方法幾乎可以越過任何內(nèi)存保護,從而成為Linux系統(tǒng)下更受歡迎的攻擊類型之一。
2.3 思考:是否需要編寫地址
上文提到的棧溢出攻擊,常常需要覆蓋函數(shù)棧幀中的返回地址,其中最重要的是攻擊者需要知道攻擊代碼的地址。另外一種思路是直接使用已經(jīng)存在的函數(shù)的地址(終止器函數(shù))將代碼放入堆棧上,從而在下次調(diào)用時執(zhí)行該代碼。這種方法不需要知道惡意代碼的地址,因此會比較高效。這種方法跟之前的方法一樣,同時也是比較受歡迎的棧溢出攻擊方式,特別是在堆棧上跳轉(zhuǎn)到快速終止器函數(shù)調(diào)用的時候。
三、防范棧溢出攻擊的方法
3.1 棧保護技術(shù)
很多操作系統(tǒng)現(xiàn)在都提供了一種在棧中加入 stack canary 的保護機制。這種機制會在棧中添加一個隨機數(shù),隨機數(shù)的值只有在函數(shù)返回時才能計算出來。這個隨機數(shù)會被保存在一次返回的信息中,因此,如果在返回時該值被修改了,那么程序就會中斷執(zhí)行,從而避免了棧溢出攻擊的發(fā)生。
3.2 編譯器選項
編譯器有一些選項可以使得棧更安全,比如可以讓編譯器自動尋找可疑的緩沖區(qū)、強制編譯器檢測函數(shù)調(diào)用的大小。
3.3 堆??臻g初始化
很多攻擊者利用指針未被初始化的漏洞來實現(xiàn)棧溢出攻擊,因此,適當?shù)某跏蓟兞糠浅S斜匾?。在一些高級語言中則更為自動化,例如 Java、Python、C# 等高級語言會將變量自動置為 null 或 0。
3.4 輸入驗證
正常的輸入應(yīng)該被驗證其完整性和大小,并且需要確保不會造成過度的數(shù)據(jù)輸入。同時,輸入驗證還可以保證程序在運行期間不會出現(xiàn)意外溢出。
3.5 限制系統(tǒng)權(quán)限
操作系統(tǒng)不需要 root 權(quán)限就可以對程序進行控制和執(zhí)行,因此操作系統(tǒng)對程序所屬的用戶、資源的權(quán)限等方面的控制也是很重要的。
對于棧溢出攻擊方面,Linux系統(tǒng)并沒有真正解決這個問題,因此,良好的程序設(shè)計、開發(fā)和實施安全措施是保護 Linux 系統(tǒng)的更佳方法。希望今天的文章能夠為大家了解 Linux 系統(tǒng)棧溢出攻擊提供一些參考,同時也幫助大家更好地應(yīng)對這種致命漏洞。
相關(guān)問題拓展閱讀:
- linux系統(tǒng)中線程同步實現(xiàn)機制有哪些
- Linux 進程棧和線程棧的區(qū)別
- 請問 怎樣分別查看windows系統(tǒng)與Linux系統(tǒng)的棧空間大???
linux系統(tǒng)中線程同步實現(xiàn)機制有哪些
LinuxThread的線程機制
LinuxThreads是目前Linux平臺上使用最為廣泛的線程庫,由Xavier Leroy () 負責開發(fā)完成,并已綁定在GLIBC中發(fā)行。它所實現(xiàn)的就是基于核心輕量級進程的”一對一”線程模型,一個線程實體對應(yīng)一個核心輕量級進程,而線程之間的 管理在核外函數(shù)庫中實現(xiàn)。
1.線程描述數(shù)據(jù)結(jié)構(gòu)及實現(xiàn)限制
LinuxThreads定義了一個struct _pthread_descr_struct數(shù)據(jù)結(jié)構(gòu)來描述線程,并使用全局數(shù)組變量 __pthread_handles來描晌胡基述和引用進程所轄線程。在__pthread_handles中的前兩項,LinuxThreads定義了兩個全 局的系統(tǒng)線程:__pthread_initial_thread和__pthread_manager_thread,并用 __pthread_main_thread表征__pthread_manager_thread的父線程(初始為 __pthread_initial_thread)。
struct _pthread_descr_struct是一個雙環(huán)鏈表結(jié)構(gòu),__pthread_manager_thread所在的鏈表僅包括它 一個元素,實際上,__pthread_manager_thread是一個特殊線程,LinuxThreads僅使用了其中的errno、p_pid、 p_priority等三個域。而__pthread_main_thread所在的鏈則將進程中所有用戶線程串在了一起。經(jīng)過一系列 pthread_create()之后形成的__pthread_handles數(shù)組將如下圖所示:
圖2 __pthread_handles數(shù)組結(jié)構(gòu)
新創(chuàng)建的線程將首先在__pthread_handles數(shù)組中占據(jù)一項,然后通過數(shù)據(jù)結(jié)構(gòu)中的鏈指針連入以__pthread_main_thread為首指針的鏈表中。這個鏈表的使用在介紹線程的創(chuàng)建和釋放的時候?qū)⑻岬健?/p>
LinuxThreads遵循POSIX1003.1c標準,其中對線程庫的實現(xiàn)進行了一些范圍限制,比如進程更大線程數(shù),線程私有數(shù)據(jù)區(qū)大小等等。在 LinuxThreads的實現(xiàn)中,基本遵循這些限制,但也進行了一定的改動,改動的趨勢是放松或者說擴大這些限制,使編程更加方便。這些限定宏主要集中 在sysdeps/unix/sysv/linux/bits/local_lim.h(不同平臺使用的文件位置不同)中,包括如下幾個:
每進程的私有數(shù)據(jù)key數(shù),POSIX定義_POSIX_THREAD_KEYS_MAX為128,LinuxThreads使用 PTHREAD_KEYS_MAX,1024;私有數(shù)據(jù)釋放時允許執(zhí)行的操作數(shù),LinuxThreads與POSIX一致,定義 PTHREAD_DESTRUCTOR_ITERATIONS為4;每進程的線程數(shù),POSIX定義為64,LinuxThreads增大到1024 (PTHREAD_THREADS_MAX);線程運行棧最小空間大小,POSIX未指定,LinuxThreads使用 PTHREAD_STACK_MIN,16384(字節(jié))。
2.管理線程
“一對一”模型的好處之一是線程的調(diào)度由核心完成了,而其他諸如線程取消、線程間的同步等工作,宴謹都是在核外線程庫中完成的。在LinuxThreads 中,專門為每一個進程構(gòu)造了一個管理線程,負責處理線程相關(guān)的管理工作。當進程之一次調(diào)用pthread_create()創(chuàng)建一個線程的時候就會創(chuàng)建 (__clone())并啟動管理線程。
在一個進程空間內(nèi),管理線程與其他線程之間通過一對”管理管道(manager_pipe)”來通訊,該管道在創(chuàng)建管理線程之前創(chuàng)建,在成功啟動 了管理線程之后,管理管道的讀端和寫端分別做姿賦給兩個全局變量__pthread_manager_reader和 __pthread_manager_request,之后,每個用戶線程都通過__pthread_manager_request向管理線程發(fā)請求, 但管理線程本身并沒有直接使用__pthread_manager_reader,管道的讀端(manager_pipe)是作為__clone ()的參數(shù)之一傳給管理線程的,管理線程的工作主要就是監(jiān)聽管道讀端,并對從中取出的請求作出反應(yīng)。
創(chuàng)建管理線程的流程如下所示:
(全局變量pthread_manager_request初值為-1)
圖3 創(chuàng)建管理線程的流程
初始化結(jié)束后,在__pthread_manager_thread中記錄了輕量級進程號以及核外分配和管理的線程id, 2*PTHREAD_THREADS_MAX+1這個數(shù)值不會與任何常規(guī)用戶線程id沖突。管理線程作為pthread_create()的調(diào)用者線程的 子線程運行,而pthread_create()所創(chuàng)建的那個用戶線程則是由管理線程來調(diào)用clone()創(chuàng)建,因此實際上是管理線程的子線程。(此處子 線程的概念應(yīng)該當作子進程來理解。)
__pthread_manager()就是管理線程的主循環(huán)所在,在進行一系列初始化工作后,進入while(1)循環(huán)。在循環(huán)中,線程以2秒為 timeout查詢(__poll())管理管道的讀端。在處理請求前,檢查其父線程(也就是創(chuàng)建manager的主線程)是否已退出,如果已退出就退出 整個進程。如果有退出的子線程需要清理,則調(diào)用pthread_reap_children()清理。
然后才是讀取管道中的請求,根據(jù)請求類型執(zhí)行相應(yīng)操作(switch-case)。具體的請求處理,源碼中比較清楚,這里就不贅述了。
3.線程棧
在LinuxThreads中,管理線程的棧和用戶線程的棧是分離的,管理線程在進程堆中通過malloc()分配一個THREAD_MANAGER_STACK_SIZE字節(jié)的區(qū)域作為自己的運行棧。
用戶線程的棧分配辦法隨著體系結(jié)構(gòu)的不同而不同,主要根據(jù)兩個宏定義來區(qū)分,一個是NEED_SEPARATE_REGISTER_STACK,這個屬 性僅在IA64平臺上使用;另一個是FLOATING_STACK宏,在i386等少數(shù)平臺上使用,此時用戶線程棧由系統(tǒng)決定具置并提供保護。與此同 時,用戶還可以通過線程屬性結(jié)構(gòu)來指定使用用戶自定義的棧。因篇幅所限,這里只能分析i386平臺所使用的兩種棧組織方式:FLOATING_STACK 方式和用戶自定義方式。
在FLOATING_STACK方式下,LinuxThreads利用mmap()從內(nèi)核空間中分配8MB空間(i386系統(tǒng)缺省的更大棧空間大小,如 果有運行限制(rlimit),則按照運行限制設(shè)置),使用mprotect()設(shè)置其中之一頁為非訪問區(qū)。該8M空間的功能分配如下圖:
圖4 棧結(jié)構(gòu)示意
低地址被保護的頁面用來監(jiān)測棧溢出。
對于用戶指定的棧,在按照指針對界后,設(shè)置線程棧頂,并計算出棧底,不做保護,正確性由用戶自己保證。
不論哪種組織方式,線程描述結(jié)構(gòu)總是位于棧頂緊鄰堆棧的位置。
4.線程id和進程id
Linux 進程棧和線程棧的區(qū)別
進程好比公交車,線程好比公交車上的人。。 一個進程可以包含多個線程,頌差當然也可以只有一個磨消線程,野游皮就是司機。。線程是任務(wù)調(diào)度單位,因為這更方便。進程 更多的是提供資源,比如進程的地址空間,所有的線程都運行在該 進程的地址空間里
返回博客列表
轉(zhuǎn) Linux 進程棧和線程棧的區(qū)別
地獄的烈火
發(fā)布時間: 2023/05/25 01:10
閱讀: 1141
收藏: 22
點贊: 0
評論: 0
注:本文所涉及的環(huán)境為Linux, 下文討論的棧跟內(nèi)核棧,沒有任何的關(guān)系,關(guān)于內(nèi)核棧,請參考《深入Linux內(nèi)核架構(gòu)前悄》中的2.4.1 進程復制
這頃信里有如下幾個問題,線程棧的空間是開辟在那里的? 線程棧之間可以互訪嗎?為什么在使用pthread_attr_setstack函數(shù)時,需要設(shè)置棧的大小,而進程task_struct的 mm_struct *mm 成員中卻并沒有卻并沒有stack_size這個成員項,怎么保存的棧大小呢?
進程棧:
進程用戶空間的管理在task_struct 的mm_struct *mm成員中體現(xiàn), mm中的成員定義了用戶空間的布局情況如圖一。 用戶空間的棧起始于STACK_TOP, 如果設(shè)置了PF_RANDOMIZE,則起始點會減少一個小的隨機量,每個體系結(jié)構(gòu)都必須定義STACK_TOP, 大多數(shù)都設(shè)置為TASK_SIZE, 在32位機上該值為0XC。經(jīng)過隨機處理后,進程棧的起始地址將存放在mm->start_stack中,可以通過cat /proc/xxx/stat 查看慧乎渣。
如圖一,棧從上而下擴展,而用于內(nèi)存映射的區(qū)域起始于mm->mmap_base, mm->mmap_base通過調(diào)用mmap_base函數(shù)來初始化,為了確保棧不與mmap區(qū)域不發(fā)生沖突,兩者之間設(shè)置了一個安全間隙。mmap_base函數(shù)源代碼如下:
#define MIN_GAP (128*1024*1024)
#define MAX_GAP (TASK_SIZE/6*5)
static inline unsigned long mmap_base(struct mm_struct *mm)
{
unsigned long gap = current->signal->rlim.rlim_cur; // rlim_cur 默認為,及8M, 可以使用 getrlimit(RLIMIT_STACK, &limit) 查看
unsigned long random_factor = 0;
if (current->flags & PF_RANDOMIZE)
random_factor = get_random_int() % (1024*1024);
if (gap MAX_GAP) // 棧的更大空間為TASK_SIZE/6*5, 及2.5G
gap = MAX_GAP;
return PAGE_ALIGN(TASK_SIZE – gap – random_factor); // 通過保留random_factor空間大小的間隙來防止棧溢出
}
圖 一 IA-32計算機上虛擬地址空間的布局
線程棧:
線程包含了表示進程內(nèi)執(zhí)行環(huán)境必需的信息,其中包括進程中標示線程的線程ID,一組寄存器值,棧,調(diào)度優(yōu)先級和策略, 信號屏蔽字,errno變量以及線程私有數(shù)據(jù)。進程的所有信息對該進程的所有線程都是共享的,包括可執(zhí)行的程序文本,程序的全局內(nèi)存和堆內(nèi)存,棧以及文件描述符,所以線程的mm_struct *mm指針變量和所屬進程的mm指針變量相同。
在創(chuàng)建線程的時候,可以通過pthread_attr_t來初始化線程的屬性,包括線程的棧布局信息,如棧起始地址stackaddr, 棧大小stacksize。 具體需要通過方法
int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize);
// 注:stackaddr 指向為該線程開辟的空間,該空間可以使用malloc或者mmap來開辟,而不能來自進程的棧區(qū)。開辟的stackaddr所指向的動態(tài)空間需要自己負責釋放。
當然也可將線程棧的空間管理交給系統(tǒng),如果想改變系統(tǒng)默認的棧大小8MB,可以通過
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
// 注:stacksize最小值為16384,單位為字節(jié)
由上面的API接口,可以得到,線程棧的stacksize是保存在pthread_attr_t中的,可以通過人為的指定,也可以通過在創(chuàng)建線程的時候讀取系統(tǒng)的配置文件來初始化stacksize,當初始化完棧的起始地址,和大小后,便可以通過
int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize);
來初始化線程棧末尾之后用以避免棧溢出的緩沖區(qū)的大小,如果應(yīng)用程序溢出到此緩沖區(qū)中,這個錯誤可能會導致 SIGSEGV 信號被發(fā)送給該線程, 從而造成段錯誤,緩沖區(qū)默認設(shè)置為PAGESIZE個字節(jié)。因為線程的mm->start_stack和所屬進程相同,所以線程棧的起始地址并沒有存放在task_struct中,應(yīng)該只是使用attr中的stackaddr,來初始化task_struct->thread-> sp(sp指向struct pt_regs對象,該結(jié)構(gòu)體用于保存用戶進程或者線程的寄存器現(xiàn)場)。
請問 怎樣分別查看windows系統(tǒng)與Linux系統(tǒng)的棧空間大???
linux和windows下同樣的文件或文件夾的大小有什么區(qū)別1.window下文件夾不算大小,linux下文件夾要算大小2.兩個系統(tǒng)下的文件系統(tǒng)可能不一樣,不同的文件系統(tǒng),blocksize可能不一樣。blocksize不一樣,文件占用的磁盤空間可能就不一樣。不同操作系統(tǒng)下查看blocksize的命令:AIX:lsfs -q /u01 Windows:fsutil fsinfo ntfsinfo c:linux:tune2fs -l /dev/sda1 3.window和弊衡linux下,文本文件租頌做的換行符不同,windows下是/n/r,linux下是/n。當使用FTP傳輸文本文件時,默認會進行換行符的轉(zhuǎn)換,造成傳輸前后文件大小不一致。4.要確認看到的文件大小是指文件本身的櫻數(shù)大小,還是文件占用的磁盤空間的大小,兩者概念不同。
linux 下:
終端輸入命令:ulimit -a
這一行就是,大概8M:stack size(kbytes, -s) 8192
windows下:
好像沒正衡有命令可以查看,但是可以通過遞歸調(diào)用函數(shù),使其首清源溢出來查看棧者態(tài)大小:
#include
void fun()
{
int a;
printf(“%p\n”,&a);
fun();
}
int main(void)
{
fun();
return 0;
}
首先,在fun()函數(shù)中的fun();處,打個斷點,得到a的
然后,去掉斷點,運行,直到棧溢出,致使程序崩潰,得到最后一個a的
兩個地址相減,即為棧的大?。杭s為 :1M
linux 經(jīng)典棧溢出的介紹就聊到這里吧,感謝你花時間閱讀本站內(nèi)容,更多關(guān)于linux 經(jīng)典棧溢出,深度解析Linux經(jīng)典棧溢出攻擊方式,linux系統(tǒng)中線程同步實現(xiàn)機制有哪些,Linux 進程棧和線程棧的區(qū)別,請問 怎樣分別查看windows系統(tǒng)與Linux系統(tǒng)的??臻g大???的信息別忘了在本站進行查找喔。
成都創(chuàng)新互聯(lián)建站主營:成都網(wǎng)站建設(shè)、網(wǎng)站維護、網(wǎng)站改版的網(wǎng)站建設(shè)公司,提供成都網(wǎng)站制作、成都網(wǎng)站建設(shè)、成都網(wǎng)站推廣、成都網(wǎng)站優(yōu)化seo、響應(yīng)式移動網(wǎng)站開發(fā)制作等網(wǎng)站服務(wù)。
網(wǎng)頁標題:深度解析Linux經(jīng)典棧溢出攻擊方式(linux經(jīng)典棧溢出)
文章源于:http://m.5511xx.com/article/ccscjcc.html


咨詢
建站咨詢
