在閱讀和學習開源作業系統時,我偶然發現了一種在匯編中呼叫“方法”的極其復雜的方法。它使用 'ret' 指令呼叫庫方法來執行此操作:
push rbp ; rsp[1] = rbp
mov rbp, .continue ; save return label to rbp
xchg rbp, QWORD [rsp] ; restore rbp and set rsp[1] to return label
push rbp ; rsp[0] = rbp
mov rbp, 0x0000700000000000 LIB_PTR_TABLE.funcOffset ; rbp = pointer to func pointer
mov rbp, QWORD [rbp] ; rbp = func pointer
xchg rbp, QWORD [rsp] ; restore rbp and set rsp[0] to func pointer
; "call" library by "returning" to the address we just planted
ret
.continue:
我添加了評論是為了自己理解它,似乎我是對的或足夠接近,因為我所做的所有實驗都成功了。但后來我嘗試這樣做,這也很完美:
mov rax, 0x0000700000000000 LIB_PTR_TABLE.funcOffset ; rax = ptr to func ptr
mov rax, QWORD [rax] ; rax = func ptr
call rax ; actually call the library function in a normal fashion
查看指令的數量以及 CPU 在兩種情況下實際必須做的事情,人們會假設,如果速度更快,它將是“呼叫”變體。但是既然使用了“ret”變體,并且首先提出這個需要大量知識,那么第一個變體有什么優勢?(或者是嗎?)
uj5u.com熱心網友回復:
隨著 CPU 變得更快,由于快取未命中和分支預測錯誤等原因導致 CPU 停頓(并且無法執行任何操作)的可能性增加。為了避免這些停頓,大多數現代 80x86 CPU 都有一堆邏輯來幫助預測控制流變化的目標地址;包括分支方向預測器、分支目標預測器、回傳堆疊緩沖區等。
問題在于惡意攻擊者(使用推測執行和測量計時)可以從 CPU 收集的所有資訊中提取機密資訊以提高性能;包括從分支方向預測器、分支目標預測器、回傳堆疊緩沖區等中提取機密資訊。
當這被發現時,人們(主要是內核開發人員)爭先恐后地想出各種方法來緩解安全問題。具體來說,尋找避免、破壞或污染 CPU 收集的資料的方法。
更具體地說(對于您顯示的代碼);如果代碼使用了call rax,那么它會將資料添加到 CPU 的回傳堆疊緩沖區,惡意攻擊者可以探測這些資料以確定有關原始值的某些資訊rax(如果rax應該是機密的,則這構成了機密性泄漏)。
一種替代方法是推送回傳地址,然后使用間接跳轉。在這種情況下,它只會將(機密)資料留在 CPU 的分支目標緩沖區中,這些資料可能會被攻擊者探測到,這并沒有真正的幫助。
ret相反,使用通過不在回傳堆疊緩沖區(或分支目標緩沖區)中存盤任何內容來防止安全問題。作為副作用,它還會“取消同步”CPU 的回傳堆疊緩沖區;混淆以前的呼叫/未來會回傳一點。
可悲的是;所有這些都會導致性能問題——它讓我們回到“隨著 CPU 變得更快,CPU 停頓的機會增加”,并且在停頓成本的基礎上增加了從錯誤地址獲取代碼的成本。
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/328585.html
