我目前使用一些代碼替換方案在32位移動到另一個位置的代碼讀取變量和類指針。由於x86_64不支持絕對尋址,因此無法在代碼的新位置獲取變量的正確地址。詳細的問題是,由於rip相對尋址,指令指針地址與編譯時不同。絕對尋址x86_64運行時代碼替換
那麼有沒有辦法在x86_64中使用絕對地址或其他方式來獲取不是相對於指令指針的變量地址?
類似於:leaq variable(%%rax), %%rbx
也會有所幫助。我只想不依賴指令指針。
我目前使用一些代碼替換方案在32位移動到另一個位置的代碼讀取變量和類指針。由於x86_64不支持絕對尋址,因此無法在代碼的新位置獲取變量的正確地址。詳細的問題是,由於rip相對尋址,指令指針地址與編譯時不同。絕對尋址x86_64運行時代碼替換
那麼有沒有辦法在x86_64中使用絕對地址或其他方式來獲取不是相對於指令指針的變量地址?
類似於:leaq variable(%%rax), %%rbx
也會有所幫助。我只想不依賴指令指針。
嘗試使用用於x86_64的代碼模型大。在gcc中,這可以通過-mcmodel = large來選擇。編譯器將爲代碼和數據使用64位絕對地址。
您還可以添加-fno-pic禁止生成與位置無關的代碼。
編輯:我建立一個小測試應用程序與-mcmodel =大產生的二進制包含像
400b81: 48 b9 f0 30 60 00 00 movabs $0x6030f0,%rcx
400b88: 00 00 00
400b8b: 49 b9 d0 09 40 00 00 movabs $0x4009d0,%r9
400b92: 00 00 00
400b95: 48 8b 39 mov (%rcx),%rdi
400b98: 41 ff d1 callq *%r9
序列這是一個絕對64位的立即的負載(在這種情況下的地址)接着是間接呼叫或間接加載。所述指令序列
moveabs variable, %rbx
addq %rax, %rbx
是相當於「leaq offset64bit(%RAX),%RBX」(不存在),與一些副作用,如標誌變更等
你問的是可行的,但不是很容易。
這樣做的一種方法是補償代碼在其指令中的移動。您需要查找所有使用RIP相對尋址的指令(它們的ModRM
字節爲05h,0dh,15h,1dh,25h,2dh,35h或3dh),並根據移動量調整它們的disp32
字段因此在虛擬地址空間中限制爲+/- 2GB,如果64位地址空間大於4GB,則可能無法保證)。
你也可以用它們的等同替換這些說明,最有可能與一個以上的替換每個原始指令,例如:
; These replace the original instruction and occupy exactly as many bytes as the original instruction:
JMP Equivalent1
NOP
NOP
Equivalent1End:
; This is the code equivalent to the original instruction:
Equivalent1:
Equivalent subinstruction 1
Equivalent subinstruction 2
...
JMP Equivalent1End
這兩種方法都需要至少一些基本的x86拆卸程序。
前者可能要求在Windows(或Linux上的某些等效項)上使用VirtualAlloc()
,以確保包含原始代碼的修補副本的內存在原始代碼的+/- 2GB範圍內。並且特定地址的分配仍可能失敗。
後者需要的不僅僅是原始的拆解,還包括全指令解碼和生成。
可能還有其他的怪癖可以解決。
指令邊界也可以通過設置RFLAGS
寄存器中的TF
標誌來找到,以使CPU在每條指令執行結束時生成single-step
調試中斷。一個調試異常處理程序將需要捕獲它們並記錄下一條指令的RIP值。我相信這可以通過在Windows中使用Structured Exception Handling (SEH)
來完成(從來沒有嘗試過使用調試中斷),不確定有關Linux。爲了這個工作,你必須讓所有的代碼都執行,每一條指令。
順便說一句,在64位模式下有絕對尋址,例如參見從0A0h到0A3h操作碼的MOV累加器指令。
感謝您的回答。據我所知,這種方法是在運行時重新計算movs的地址值。這看起來很重要的計劃,所以如果這是我會考慮一個不同的實施 – nux
-mcmodel =大的唯一方法應該是解決方案。我必須調查爲什麼gcc osx編譯器不支持它 – nux
也許是舊的。小型(標準)和中型代碼模型被提前添加,大型模型出現較晚。 – hirschhornsalz
我非常感謝你,這看起來非常好,明確地解決絕對尋址問題 – nux