union all动态表_深入窥探动态链接
生活随笔
收集整理的這篇文章主要介紹了
union all动态表_深入窥探动态链接
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
本文為看雪論壇優(yōu)秀文章看雪論壇作者ID:1Oin00x00 前言本文主要分析了在延遲綁定中,調(diào)用某函數(shù)之后如何找到正確的地址。文章中深入的分析了這個(gè)過程,并且分析完之后針對(duì)該鏈接介紹了一些攻擊手法和程序所作的一些保護(hù)。0x01?基礎(chǔ)知識(shí)
動(dòng)態(tài)符號(hào)表,存儲(chǔ)著在動(dòng)態(tài)鏈接中所需要的每個(gè)函數(shù)所對(duì)應(yīng)的符號(hào)信息,每個(gè)結(jié)構(gòu)體分別對(duì)應(yīng)一個(gè)符號(hào) (函數(shù)) 。結(jié)構(gòu)體數(shù)組。d_tag = DT_SYMTAB(值為 0x6) 的節(jié)。(2)結(jié)構(gòu):typedef struct{ Elf64_Word st_name; /* Symbol name (string tbl index) */ // 保存著該函數(shù)函數(shù)名在 .dynstr 中的偏移,可以結(jié)合 .dynstr 找到準(zhǔn)確函數(shù)名。 unsigned char st_info; /* Symbol type and binding */ unsigned char st_other; /* Symbol visibility */ Elf64_Section st_shndx; /* Section index */ Elf64_Addr st_value; /* Symbol value */ // 如果這個(gè)符號(hào)被導(dǎo)出,則存有這個(gè)導(dǎo)出函數(shù)的虛擬地址,否則為NULL. Elf64_Xword st_size; /* Symbol size */} Elf64_Sym;
【答案 1】拿到這個(gè) reloc_arg 后,鏈接器會(huì)通過該值找到對(duì)應(yīng)函數(shù)的 Elf_Rel 結(jié)構(gòu),通過該結(jié)構(gòu)的 r_info 變量中的偏移量找到對(duì)應(yīng)函數(shù)的 Elf_Sym 結(jié)構(gòu),然后再通過 Elf_Sym 結(jié)構(gòu)的 st_name 結(jié)合之前已經(jīng)確定的 .dynstr 地址,通過 st_name + .dynstr 獲得對(duì)應(yīng)函數(shù)的函數(shù)名。這就是拿到 reloc_arg 參數(shù)后鏈接器獲得的信息,即知道了本次鏈接中的函數(shù)的函數(shù)名。(注:此處用到的 binary 中的 Elf_Rel Elf_Sym .dynstr 等地址都是通過 link_map->l_info[x] 的方式尋找的。)
【答案 2】拿到這個(gè)變量后鏈接器會(huì)獲得所要解析的函數(shù)的函數(shù)庫(kù)(通過 link_map 的 l_next 字段),然后拿到這個(gè)外部庫(kù)之后 link_map 的 l_addr 字段會(huì)記錄該庫(kù)的基地址,然后鏈接器通過 new_hash 函數(shù)求出要鏈接函數(shù)的 hash(new_hash(st_name + .dynstr)),然后通過該 hash 和之前的保存值進(jìn)行匹配,如果匹配上就獲得了該函數(shù)在外部庫(kù)的 Elf64_Sym 結(jié)構(gòu),然后通過該結(jié)構(gòu)的 st_value 獲取該函數(shù)在外部庫(kù)里面的偏移,最后通過 st_value + l_addr 獲取該函數(shù)的真實(shí)地址,最后通過 Elf64_Rel 的 r_offset 定位該函數(shù)在 GOT 中對(duì)應(yīng)的地址,然后將最后結(jié)果寫入該地址中。(其中有通過這兩個(gè)參數(shù)共同獲得的東西,不過為了便于理解就不再分開討論。)0x03?攻擊
動(dòng)態(tài)裝載器是從 .dynamic 段的 DT_STRTAB 條目中獲得 .dynstr 段的地址的,而且 DT_STRTAB 條目的位置是已知的,默認(rèn)情況下也可寫。我們可以將這個(gè)條目的 d_val 域覆蓋為 .bss 段。這塊內(nèi)存區(qū)域上將會(huì)包含一段字符串,比如 system。到了這一步,攻擊者需要選擇一個(gè)已經(jīng)存在的符號(hào),它的偏移在偽造的字符串表中正好指向 system 的位置,接著調(diào)用其對(duì)應(yīng)的符號(hào)解析重定位過程。可以通過將其重定位項(xiàng)的偏移壓棧并跳轉(zhuǎn)到 PLT0 實(shí)現(xiàn)。限制這種方式非常簡(jiǎn)單,但僅當(dāng)二進(jìn)制程序的 .dynamic 段可寫時(shí)有效。對(duì)于使用部分或完全 RELRO 編譯的二進(jìn)制程序,需要使用更復(fù)雜的攻擊。
_dl_runtime_resolve 函數(shù)的第二個(gè)參數(shù)是 Elf_Rel 條目在 .rel.plt 段中對(duì)應(yīng)當(dāng)前請(qǐng)求函數(shù)的偏移。動(dòng)態(tài)裝載器將這個(gè)值加上 .rel.plt 的基地址來得到目標(biāo) Elf_Rel 結(jié)構(gòu)的絕對(duì)地址。然而多數(shù)動(dòng)態(tài)裝載器實(shí)現(xiàn)不去檢查重定位表的邊界。這就表明如果一個(gè)大于 .rel.plt 的值傳到 _dl_runtime_resolve 中,裝載器將會(huì)認(rèn)為特定的地址上的數(shù)據(jù)是一個(gè) Elf_Rel 結(jié)構(gòu)并使用它,即使那里已經(jīng)超出了.rel .plt段的范圍。過程
計(jì)算一個(gè)新的 reloc_arg 參數(shù),將 _dl_runtime_resolve 解析的位置劫持到一個(gè)可控內(nèi)存。然后在那里構(gòu)造一個(gè) Elf_Rel 結(jié)構(gòu),并填寫 r_offset 的值為一個(gè)可寫的內(nèi)存地址,將最后解析出的函數(shù)地址寫在那里。同時(shí),r_info 也要修改成一個(gè)可控區(qū)域處。并在該區(qū)域偽造一個(gè) Elf_Sym 結(jié)構(gòu),其中的 st_name 域,指向另一個(gè)可控區(qū)域,并在該處填寫要偽造成的函數(shù)名(例:system)。
簡(jiǎn)而言之,該過程偽造了函數(shù)鏈接中所需要的所有結(jié)構(gòu)(Elf_Sym Elf_Rel .dynstr),通過控制 reloc_arg 指向到偽造的 Elf_Rel ,再通過Elf_Rel 中的 r_info 找到偽造的 Elf_Sym 最后通過 Elf_Sym 的 st_name 找到最終偽造后需要解析的函數(shù)(例:system),解析完后通過 Elf_Rel 的 r_offset 寫回到正確位置,達(dá)到劫持函數(shù)解析的目的,最終執(zhí)行自己想要執(zhí)行的函數(shù)。限制首先,Elf_Rel 的下標(biāo)需要是正數(shù),因?yàn)?r_info 域在 ELF 標(biāo)準(zhǔn)中規(guī)定是一個(gè)無符號(hào)整數(shù)。這就意味著在實(shí)際中這塊可寫的內(nèi)存空間(例如.bss段)必須是位于 .dynsym 段之后。這種情況總是滿足的。另一個(gè)限制是 ELF 會(huì)使用的符號(hào)版本系統(tǒng)。在這種情況下,Elf_Rel 的 r_info 域不僅用作動(dòng)態(tài)符號(hào)表中的下標(biāo),也用作符號(hào)版本表(.gnu.version段)中的下標(biāo)。擴(kuò)充方法可以通過修改指向程序那一層的 link_map,具體做法是把該層 link_map->l_info[DT_STRTAB]->st_value 的值劫持到一個(gè)我們可控的區(qū)域,然后在該區(qū)域填充偽造函數(shù),其實(shí)該方法也是通過修改 .dynstr 的方式實(shí)現(xiàn)攻擊的手法。不過該方法必須有能夠改寫 st_value 值所需要的 gadget。
:)?bilibili 視頻(https://www.bilibili.com/video/av17482224)0x05?總結(jié)第一次在論壇發(fā)文章,文章中可能有錯(cuò)誤或不恰當(dāng)?shù)牡胤?#xff0c;如果有發(fā)現(xiàn)歡迎各位大佬批評(píng)指正,同時(shí)歡迎各位道友交流探討。- End?-
>>>>
動(dòng)態(tài)鏈接
在動(dòng)態(tài)鏈接方式實(shí)現(xiàn)以前,普遍采用靜態(tài)鏈接的方式來生成可執(zhí)行文件。如果一個(gè)程序使用了外部的庫(kù)函數(shù),那么整個(gè)庫(kù)都會(huì)被直接編譯到可執(zhí)行文件中。ELF 支持動(dòng)態(tài)鏈接,這在處理共享庫(kù)的時(shí)候就會(huì)非常高效。當(dāng)一個(gè)程序被加載進(jìn)內(nèi)存時(shí),動(dòng)態(tài)鏈接器會(huì)把需要的共享庫(kù)加載并綁定到該進(jìn)程的地址空間中。隨后在調(diào)用某個(gè)函數(shù)時(shí),對(duì)該函數(shù)地址進(jìn)行解析,以達(dá)到對(duì)該函數(shù)調(diào)用的目的。>>>>
兩個(gè)表
1. PLT表(Procedure Linkage Table)
(1)簡(jiǎn)介:?全局偏移表,在程序中以 .plt 節(jié)表示,該表處于代碼段,每一個(gè)表項(xiàng)表示了一個(gè)與要重定位的函數(shù)相關(guān)的若干條指令,每個(gè)表項(xiàng)長(zhǎng)度為 16 個(gè)字節(jié),存儲(chǔ)的是用于做延遲綁定的代碼。(2)結(jié)構(gòu)簡(jiǎn)介:PLT[0] --> 與每個(gè)函數(shù)第一次鏈接相關(guān)指令例:0x4004c0:0x4004c0: ff 35 42 0b 20 00 push QWORD PTR [rip+0x200b42] // push [GOT[1]]0x4004c6: ff 25 44 0b 20 00 jmp QWORD PTR [rip+0x200b44] // jmp [GOT[2]]0x4004cc: 0f 1f 40 00 nop DWORD PTR [rax+0x0]即: 第一條指令為 push 一個(gè)值,該值為 GOT[1] 處存放的地址, 第二條指令為 jmp 到一個(gè)地址執(zhí)行,該值為 GOT[2] 處存放的地址PLT[1] --> 某個(gè)函數(shù)鏈接時(shí)所需要的指令,與 got 表一一對(duì)應(yīng)例:0x4004d0 <__stack_chk_fail>:0x4004d0: ff 25 42 0b 20 00 jmp QWORD PTR [rip+0x200b42] // jmp GOT[3] 0x4004d6: 68 00 00 00 00 push 0x0 // push reloc_arg0x4004db: e9 e0 ff ff ff jmp 0x4004c0 <_init> // jmp PLT[0]即: 第一條指令為: jmp 到一個(gè)地址執(zhí)行,該地址為對(duì)應(yīng) GOT 表項(xiàng)處存放的地址,在下文中會(huì)具體討論這種結(jié)構(gòu) 第二條指令為: push 一個(gè)值,該值作用在下文提到 第三個(gè)指令為: jmp 一個(gè)地址執(zhí)行,其實(shí)該地址就是上邊提到的 PLT[0] 的地址, 也就是說接下來要執(zhí)行 PLT[0] 中保存的兩條指令 . . .2. GOT表(Global Offset Table)?
(1)簡(jiǎn)介:過程連接表,在程序中以 .got.plt 表示,該表處于數(shù)據(jù)段,每一個(gè)表項(xiàng)存儲(chǔ)的都是一個(gè)地址,每個(gè)表項(xiàng)長(zhǎng)度是當(dāng)前程序的對(duì)應(yīng)需要尋址長(zhǎng)度(32位程序:4字節(jié),64位程序:8字節(jié))。d_tag = DT_PLTGOT(2)結(jié)構(gòu)簡(jiǎn)介:GOT[0] --> 此處存放的是 .dynamic 的地址;該節(jié)(段)的作用會(huì)在下文討論GOT[1] --> 此處存放的是 link_map 的地址;該結(jié)構(gòu)也會(huì)在下文討論GOT[2] --> 此處存放的是 dl_runtime_resolve 函數(shù)的地址GOT[3] --> 與 PLT[1] 對(duì)應(yīng),存放的是與該表項(xiàng) (PLT[1]) 要解析的函數(shù)相關(guān)地址, 由于延遲綁定的原因,開始未調(diào)用對(duì)應(yīng)函數(shù)時(shí)該項(xiàng)存的是 PLT[1] 中第二條指令的地址, 當(dāng)進(jìn)行完一次延遲綁定之后存放的才是所要解析的函數(shù)的真實(shí)地址GOT[4] --> 與 PLT[2] 對(duì)應(yīng),所以存放的是與 PLT[2] 所解析的函數(shù)相關(guān)的地址 . . .3. 兩個(gè)表之間的關(guān)系
GOT[0]: .dynamic 地址 PLT[0]: 與每個(gè)函數(shù)第一次鏈接相關(guān)指令GOT[1]: link_map 地址GOT[2]: dl_runtime_resolve 函數(shù)地址 GOT[3] --> PLT[1] // 一一對(duì)應(yīng)GOT[4] --> PLT[2] // 相互協(xié)同,作用于一個(gè)函數(shù)GOT[5] --> PLT[3] // 一個(gè)保存的是該函數(shù)所需要的延遲綁定的指令GOT[6] --> PLT[4] // 一個(gè)是保存?zhèn)€該函數(shù)鏈接所需要的地址 . . . . . .>>>>
一個(gè)段(節(jié))三個(gè)節(jié)
在下面只對(duì)一些接下來要用到的結(jié)構(gòu)體成員做一些中文解釋。1. .dynmic
(1)介紹:因?yàn)樵诩虞d過程中,.dynmic 節(jié)整個(gè)以一個(gè)段的形式加載進(jìn)內(nèi)存,所以說在程序中的 .dynmic 節(jié)也就是運(yùn)行后的 .dynmic 段。該段主要與動(dòng)態(tài)鏈接的整個(gè)過程有關(guān),所以保存的是與動(dòng)態(tài)鏈接相關(guān)信息,此處主要用于尋找與動(dòng)態(tài)鏈接相關(guān)的其他節(jié)( .dynsym .dynstr .rela.plt 等節(jié))。該段保存了許多 Elf64_Dyn 結(jié)構(gòu),該數(shù)據(jù)結(jié)構(gòu)保存了一些其他節(jié)的信息。下面展示該段所保存的數(shù)據(jù)結(jié)構(gòu)。p_type = PT_DYNAMIC(值為 0x2)的段。(2)結(jié)構(gòu):// 該結(jié)構(gòu)都有 64 位程序和 32 位程序的區(qū)別,不過大致結(jié)構(gòu)相似,此處只討論 64 位程序中的// /usr/include/elf.h typedef struct { Elf64_Sxword d_tag; /* Dynamic entry type */ // d_tag 識(shí)別該結(jié)構(gòu)體表示的哪一個(gè)節(jié),通過以此字段不同來尋找不同的節(jié) union { Elf64_Xword d_val; /* Integer value */ // 對(duì)應(yīng)節(jié)的地址,用于存儲(chǔ)該結(jié)構(gòu)體表示下的節(jié)所在的地址 Elf64_Addr d_ptr; /* Address value */ // 一般于上一個(gè)字段表示的值相同,所以筆者現(xiàn)在并不了解他們的區(qū)別 } d_un; } Elf64_Dyn;2. .dynsym
(1)介紹:動(dòng)態(tài)符號(hào)表,存儲(chǔ)著在動(dòng)態(tài)鏈接中所需要的每個(gè)函數(shù)所對(duì)應(yīng)的符號(hào)信息,每個(gè)結(jié)構(gòu)體分別對(duì)應(yīng)一個(gè)符號(hào) (函數(shù)) 。結(jié)構(gòu)體數(shù)組。d_tag = DT_SYMTAB(值為 0x6) 的節(jié)。(2)結(jié)構(gòu):typedef struct{ Elf64_Word st_name; /* Symbol name (string tbl index) */ // 保存著該函數(shù)函數(shù)名在 .dynstr 中的偏移,可以結(jié)合 .dynstr 找到準(zhǔn)確函數(shù)名。 unsigned char st_info; /* Symbol type and binding */ unsigned char st_other; /* Symbol visibility */ Elf64_Section st_shndx; /* Section index */ Elf64_Addr st_value; /* Symbol value */ // 如果這個(gè)符號(hào)被導(dǎo)出,則存有這個(gè)導(dǎo)出函數(shù)的虛擬地址,否則為NULL. Elf64_Xword st_size; /* Symbol size */} Elf64_Sym;
3. .dynstr
(1)介紹:動(dòng)態(tài)字符串表,表中存放了一系列字符串,這些字符串代表了符號(hào)的名稱,在此處可以看成函數(shù)名,以空字符作為終止符。該結(jié)構(gòu)是一個(gè)字符串?dāng)?shù)組。d_tag = DT_STRTAB(值為 0x5) 的節(jié)。(2)結(jié)構(gòu):一個(gè)字符串?dāng)?shù)組4. .rel.plt (.rela.plt)
(1)介紹:重定位節(jié),保存了重定位相關(guān)的信息,這些信息描述了如何在鏈接或者運(yùn)行時(shí),對(duì) ELF 目標(biāo)文件的某部分內(nèi)容或者進(jìn)程鏡像進(jìn)行補(bǔ)充或修改。每個(gè)結(jié)構(gòu)體也與某一個(gè)重定位的函數(shù)相關(guān)。結(jié)構(gòu)體數(shù)組。d_tag = DT_REL(值為 0x11) / d_tag = DT_RELA(值為 0x7) 的節(jié)。(2)結(jié)構(gòu):typedef struct{ Elf64_Addr r_offset; /* Address */ // 此處表示的是解析完的函數(shù)真實(shí)地址存放的位置, // 即對(duì)應(yīng)解析函數(shù)的 GOT 表項(xiàng)地址 Elf64_Xword r_info; /* Relocation type and symbol index */ // 該結(jié)構(gòu)主要用到高某位,表示索引,低位表示類型 // 例如:0x10000007 此處 1 表示索引,7 代表類型,主要用到 1 值,還記得上邊在 PLT 中的指令嘛? //每一個(gè)表項(xiàng)的第二條指令, PUSH 了一個(gè)索引,所 PUSH 的索引與此相關(guān), //也就是通過 PLT 中 PUSH 的索引找到當(dāng)時(shí)解析的函數(shù)對(duì)應(yīng)的此結(jié)構(gòu)體的} Elf64_Rel;//與上一結(jié)構(gòu)體類似,只是不同編譯環(huán)境下產(chǎn)生的不同結(jié)構(gòu),作用相同,就不再次討論 typedef struct { Elf32_Addr r_offset; /* Address */ Elf32_Word r_info; /* Relocation type and symbol index */ Elf32_Sword r_addend; /* Addend */ } Elf32_Rela;>>>>
擴(kuò)充結(jié)構(gòu)體(在Full RELRO用到)
本節(jié)討論的是在 Full RELRO 攻擊中用到的結(jié)構(gòu),所以如果不打算研究該攻擊手法可以跳過該節(jié)。d_tag = DT_DEBUG。struct r_debug{ //由于并沒有找到該結(jié)構(gòu)體的定義,所以沒有聲明類型 r_version r_map //指向 link_map r_brk r_state r_ldbase}>>>>
link map?結(jié)構(gòu)
1. 簡(jiǎn)介
保存著 Binary 里面所有信息的一個(gè)結(jié)構(gòu)體,該結(jié)構(gòu)體很大,內(nèi)容豐富。2.主要字段
l_next:鏈接著該程序所有用到的 libary 上邊提到的 GOT[1] 中保存的地址是第一層 link_map 中所表示的 libary,此時(shí)是指向的程序本身, 不過可以用 l_next 結(jié)構(gòu)尋找下一層表示的 libary,以此來遍歷程序中所用到的 libary, 并利用下邊所提到的字段找到該層 libary 的名字、基地址、以及所有的 section 等信息。l_name:表示 libary 的名字l_addr:表示 libary 的基地址l_info[x]:指向該 libary 下的 .dynamic。 l_info[1] 指向 d_tag = 1 時(shí)所表示的 section ,所以可以改變 x 的值找到每個(gè)相關(guān) section 的地址。 在鏈接過程中 binary 中的 section 地址,以及 libary 中的地址都是通過此方法確定的。0x02?鏈接過程>>>>
概括描述
完成延遲綁定的函數(shù)主要是 dl_runtime__resolve(link_map_obj, reloc_arg) ,該函數(shù)的第一個(gè)參數(shù)是一個(gè) link_map 結(jié)構(gòu),第二個(gè)參數(shù)是一個(gè)重定位參數(shù),即運(yùn)行 PLT 中的代碼時(shí) PUSH 進(jìn)棧中的參數(shù)。該函數(shù)主要是調(diào)用一個(gè) dl_fixup(link_map_obj, reloc_arg) 完成了主要功能。參數(shù)一的主要作用是:獲得重定位函數(shù)所在了 libary 的基地址,以及獲取在 libary 中尋找需要定位函數(shù)時(shí)所需要的 Section (.dynstr .dynsym 等)。第二個(gè)函數(shù)主要是確定需要解析的函數(shù)名,以及解析完之后寫回的地址。該過程可以先大概理解為,dl_fixup 函數(shù)通過 reloc_arg 參數(shù)確定當(dāng)前正在解析的函數(shù)名。之后,拿著這個(gè)函數(shù)名,再利用 link_map 結(jié)構(gòu)找到 libary 中的 .dynsym .dynstr 。利用 .dynsym .dynstr 進(jìn)行匹配。若匹配成功,則從 .dynsym 中獲取該函數(shù)的函數(shù)地址。// 上邊的詳細(xì)過程reloc_arg --> 函數(shù)名 A利用 link_map --> l_info[x] 通過改變 x 的值,確定 .dynsym .dynstr再用 .dynsym 與 .dynstr 對(duì)整個(gè)動(dòng)態(tài)符號(hào)表 .dynstym 進(jìn)行遍歷,去匹配函數(shù)名 A若 某一個(gè) Elf64_Sym(符號(hào)) 的 st_name + .dynstr == A則 該 Elf64_Sym 表示的符號(hào)即為函數(shù) A// 整個(gè)過程可以這樣理解,不過真實(shí)情況使用的 Hash 方法去尋找的這個(gè) Elf64_Sym(符號(hào))>>>>
具體過程
1. 調(diào)用某個(gè)函數(shù)后進(jìn)入該函數(shù)的 PLT[x] ,在 PLT[x] 中 push 一個(gè)參數(shù) reloc_arg 。
【問題 1】通過這個(gè) reloc_arg 可以干什么?【答案 1】拿到這個(gè) reloc_arg 后,鏈接器會(huì)通過該值找到對(duì)應(yīng)函數(shù)的 Elf_Rel 結(jié)構(gòu),通過該結(jié)構(gòu)的 r_info 變量中的偏移量找到對(duì)應(yīng)函數(shù)的 Elf_Sym 結(jié)構(gòu),然后再通過 Elf_Sym 結(jié)構(gòu)的 st_name 結(jié)合之前已經(jīng)確定的 .dynstr 地址,通過 st_name + .dynstr 獲得對(duì)應(yīng)函數(shù)的函數(shù)名。這就是拿到 reloc_arg 參數(shù)后鏈接器獲得的信息,即知道了本次鏈接中的函數(shù)的函數(shù)名。(注:此處用到的 binary 中的 Elf_Rel Elf_Sym .dynstr 等地址都是通過 link_map->l_info[x] 的方式尋找的。)
2. 在鏈接過程中 PLT[0] 會(huì) push dl_runtime_resolve 函數(shù)的第二個(gè)參數(shù) link_map。
【問題 2】通過 link_map 我們能獲得什么?【答案 2】拿到這個(gè)變量后鏈接器會(huì)獲得所要解析的函數(shù)的函數(shù)庫(kù)(通過 link_map 的 l_next 字段),然后拿到這個(gè)外部庫(kù)之后 link_map 的 l_addr 字段會(huì)記錄該庫(kù)的基地址,然后鏈接器通過 new_hash 函數(shù)求出要鏈接函數(shù)的 hash(new_hash(st_name + .dynstr)),然后通過該 hash 和之前的保存值進(jìn)行匹配,如果匹配上就獲得了該函數(shù)在外部庫(kù)的 Elf64_Sym 結(jié)構(gòu),然后通過該結(jié)構(gòu)的 st_value 獲取該函數(shù)在外部庫(kù)里面的偏移,最后通過 st_value + l_addr 獲取該函數(shù)的真實(shí)地址,最后通過 Elf64_Rel 的 r_offset 定位該函數(shù)在 GOT 中對(duì)應(yīng)的地址,然后將最后結(jié)果寫入該地址中。(其中有通過這兩個(gè)參數(shù)共同獲得的東西,不過為了便于理解就不再分開討論。)0x03?攻擊
>>>>
保護(hù)手段(RELRO)
RELRO:重定位只讀手段1.無保護(hù)
在這種模式下關(guān)于重定位并不進(jìn)行任何保護(hù)。2.部分保護(hù)
在這種模式下,一些段 (包括.dynamic) 在初始化后將會(huì)被標(biāo)識(shí)為只讀。3.完全保護(hù)
在這種模式下,除了會(huì)開啟部分保護(hù)外。惰性解析會(huì)被禁用(所有的導(dǎo)入符號(hào)將在開始時(shí)被解析,.got.plt 段會(huì)被完全初始化為目標(biāo)函數(shù)的終地址,并被標(biāo)記為只讀)。此外,既然惰性解析被禁用,GOT[1] 與 GOT[2] 條目將不會(huì)被初始化,存值為0。>>>>
對(duì)應(yīng)攻擊方法
動(dòng)態(tài)裝載器認(rèn)為它接收到的參數(shù)都是值得信任的,因?yàn)樗僭O(shè)這些都是直接由 ELF 文件提供的或者是它自己在開始時(shí)初始化的。然而,當(dāng)一個(gè)攻擊者能夠修改這些數(shù)據(jù)時(shí),這個(gè)假設(shè)就不成立 了。一些動(dòng)態(tài)裝載器(FreeBSD)會(huì)驗(yàn)證自己接收到的輸入。然而,他們還是完全地信任控制結(jié)構(gòu) ,但這些也會(huì)可以輕易地破壞。1. 無保護(hù)
原理動(dòng)態(tài)裝載器從 .rel.plt 中的 Elf_Rel 結(jié)構(gòu)開始工作,順著其中的下標(biāo)找到 .dynsym 段中對(duì)應(yīng) Elf_Sym 結(jié)構(gòu)的位置,并終使用它確定待解析符號(hào)的名稱(在 .dynstr 段中的一段字符串)。簡(jiǎn)單的調(diào)用任意函數(shù)的辦法就是使用希望的函數(shù)的名稱覆蓋字符串表中的條目 ,然后再調(diào)用動(dòng)態(tài)裝載器,但這是不可能的,因?yàn)楸4嬷鴦?dòng)態(tài)符號(hào)字符串表的段,即.dynstr,是不可寫的。過程動(dòng)態(tài)裝載器是從 .dynamic 段的 DT_STRTAB 條目中獲得 .dynstr 段的地址的,而且 DT_STRTAB 條目的位置是已知的,默認(rèn)情況下也可寫。我們可以將這個(gè)條目的 d_val 域覆蓋為 .bss 段。這塊內(nèi)存區(qū)域上將會(huì)包含一段字符串,比如 system。到了這一步,攻擊者需要選擇一個(gè)已經(jīng)存在的符號(hào),它的偏移在偽造的字符串表中正好指向 system 的位置,接著調(diào)用其對(duì)應(yīng)的符號(hào)解析重定位過程。可以通過將其重定位項(xiàng)的偏移壓棧并跳轉(zhuǎn)到 PLT0 實(shí)現(xiàn)。限制這種方式非常簡(jiǎn)單,但僅當(dāng)二進(jìn)制程序的 .dynamic 段可寫時(shí)有效。對(duì)于使用部分或完全 RELRO 編譯的二進(jìn)制程序,需要使用更復(fù)雜的攻擊。
2. 部分保護(hù)
原理_dl_runtime_resolve 函數(shù)的第二個(gè)參數(shù)是 Elf_Rel 條目在 .rel.plt 段中對(duì)應(yīng)當(dāng)前請(qǐng)求函數(shù)的偏移。動(dòng)態(tài)裝載器將這個(gè)值加上 .rel.plt 的基地址來得到目標(biāo) Elf_Rel 結(jié)構(gòu)的絕對(duì)地址。然而多數(shù)動(dòng)態(tài)裝載器實(shí)現(xiàn)不去檢查重定位表的邊界。這就表明如果一個(gè)大于 .rel.plt 的值傳到 _dl_runtime_resolve 中,裝載器將會(huì)認(rèn)為特定的地址上的數(shù)據(jù)是一個(gè) Elf_Rel 結(jié)構(gòu)并使用它,即使那里已經(jīng)超出了.rel .plt段的范圍。過程
計(jì)算一個(gè)新的 reloc_arg 參數(shù),將 _dl_runtime_resolve 解析的位置劫持到一個(gè)可控內(nèi)存。然后在那里構(gòu)造一個(gè) Elf_Rel 結(jié)構(gòu),并填寫 r_offset 的值為一個(gè)可寫的內(nèi)存地址,將最后解析出的函數(shù)地址寫在那里。同時(shí),r_info 也要修改成一個(gè)可控區(qū)域處。并在該區(qū)域偽造一個(gè) Elf_Sym 結(jié)構(gòu),其中的 st_name 域,指向另一個(gè)可控區(qū)域,并在該處填寫要偽造成的函數(shù)名(例:system)。
簡(jiǎn)而言之,該過程偽造了函數(shù)鏈接中所需要的所有結(jié)構(gòu)(Elf_Sym Elf_Rel .dynstr),通過控制 reloc_arg 指向到偽造的 Elf_Rel ,再通過Elf_Rel 中的 r_info 找到偽造的 Elf_Sym 最后通過 Elf_Sym 的 st_name 找到最終偽造后需要解析的函數(shù)(例:system),解析完后通過 Elf_Rel 的 r_offset 寫回到正確位置,達(dá)到劫持函數(shù)解析的目的,最終執(zhí)行自己想要執(zhí)行的函數(shù)。限制首先,Elf_Rel 的下標(biāo)需要是正數(shù),因?yàn)?r_info 域在 ELF 標(biāo)準(zhǔn)中規(guī)定是一個(gè)無符號(hào)整數(shù)。這就意味著在實(shí)際中這塊可寫的內(nèi)存空間(例如.bss段)必須是位于 .dynsym 段之后。這種情況總是滿足的。另一個(gè)限制是 ELF 會(huì)使用的符號(hào)版本系統(tǒng)。在這種情況下,Elf_Rel 的 r_info 域不僅用作動(dòng)態(tài)符號(hào)表中的下標(biāo),也用作符號(hào)版本表(.gnu.version段)中的下標(biāo)。擴(kuò)充方法可以通過修改指向程序那一層的 link_map,具體做法是把該層 link_map->l_info[DT_STRTAB]->st_value 的值劫持到一個(gè)我們可控的區(qū)域,然后在該區(qū)域填充偽造函數(shù),其實(shí)該方法也是通過修改 .dynstr 的方式實(shí)現(xiàn)攻擊的手法。不過該方法必須有能夠改寫 st_value 值所需要的 gadget。
3. 完全保護(hù)
原理DT_DEBUG 條目的值是動(dòng)態(tài)裝載器在加載時(shí)設(shè)置好的,它指向一個(gè) r_debug 類型的數(shù)據(jù)結(jié)構(gòu)。這個(gè)數(shù)據(jù)結(jié)構(gòu)保存著調(diào)試 器用來標(biāo)識(shí)動(dòng)態(tài)裝載器的基地址并攔截相應(yīng)事件需要的信息。此外,這個(gè)結(jié)構(gòu)的 r_map 域保存著一個(gè)指向 link_map 鏈表頭部的指針。過程攻擊者使用 DT_DEBUG 這個(gè)動(dòng)態(tài)條目來獲取 r_debug 結(jié)構(gòu)。接著,解引用 r_map 域從而得到主程序的 link_map 結(jié)構(gòu)。然后像上邊擴(kuò)充方法那樣破壞 l_info[DT_STRTAB]。接著攻擊者同樣需要恢復(fù) _dl_runtime_resolve 函數(shù)的指針,通過 link_map->l_next 獲取其他鏈接庫(kù)中用到的該函數(shù)。具體獲取手法是攻擊者通過 l_info[DT_PLTGOT] 來獲取對(duì)應(yīng)的符號(hào),然后通過 st_value 獲取 .plt.got 節(jié)所在的地址,前面討論過,該節(jié)的第三個(gè)偏移所存放的內(nèi)容即為 _dl_runtime_resolve 函數(shù)地址。不過在這一切都做好之后,還有一個(gè)問題值得關(guān)注,_dl_runtime_resolve 不僅僅會(huì)調(diào)用目標(biāo)函數(shù),還會(huì)嘗試將它的地址寫到 GOT 項(xiàng)中。因?yàn)橥耆?RELRO 保護(hù)下 GOT 是不可寫的,所以程序就會(huì)崩潰。不過我們可以通過偽造 link_map 中的 DT_JMPREL 動(dòng)態(tài)條目來繞過這個(gè)問題。(具體操作方式和擴(kuò)充方法中修改 .dynstr 類似)原本 DT_JMPREL 指向 .rel.dyn 段,我們將其改為一塊可控區(qū)域,并那里寫有一個(gè) Elf_Rel 結(jié)構(gòu),并將其 r_offset 域指向一塊可寫的內(nèi)存區(qū)域,其 r_info 指向我們的目標(biāo)符號(hào)。至此,我們就完成了整個(gè)攻擊過程。0x04?參考:) paper:sec15-paper-di-frederico:)?bilibili 視頻(https://www.bilibili.com/video/av17482224)0x05?總結(jié)第一次在論壇發(fā)文章,文章中可能有錯(cuò)誤或不恰當(dāng)?shù)牡胤?#xff0c;如果有發(fā)現(xiàn)歡迎各位大佬批評(píng)指正,同時(shí)歡迎各位道友交流探討。- End?-
看雪ID:1Oin0
https://bbs.pediy.com/user-864295.htm?
*這里由看雪論壇 1Oin0 原創(chuàng),轉(zhuǎn)載請(qǐng)注明來自看雪社區(qū)。推薦文章++++
*??為了理解反匯編引擎而寫的X86 / X64反匯編引擎
*??捆綁包驅(qū)動(dòng)鎖首病毒分析
*??**游戲逆向分析筆記
*??對(duì)寶馬車載apps協(xié)議的逆向分析研究
*??x86_64架構(gòu)下的函數(shù)調(diào)用及棧幀原理
好書推薦
﹀﹀﹀公眾號(hào)ID:ikanxue官方微博:看雪安全商務(wù)合作:wsc@kanxue.com戳“閱讀原文”一起來充電吧!總結(jié)
以上是生活随笔為你收集整理的union all动态表_深入窥探动态链接的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python退出循环快捷_python退
- 下一篇: 编译器不识别stm指令_编译器简介