2011-11-02 38 views
8

我目前使用一些代碼替換方案在32位移動到另一個位置的代碼讀取變量和類指針。由於x86_64不支持絕對尋址,因此無法在代碼的新位置獲取變量的正確地址。詳細的問題是,由於rip相對尋址,指令指針地址與編譯時不同。絕對尋址x86_64運行時代碼替換

那麼有沒有辦法在x86_64中使用絕對地址或其他方式來獲取不是相對於指令指針的變量地址?

類似於:leaq variable(%%rax), %%rbx也會有所幫助。我只想不依賴指令指針。

回答

6

嘗試使用用於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」(不存在),與一些副作用,如標誌變更等

+0

-mcmodel =大的唯一方法應該是解決方案。我必須調查爲什麼gcc osx編譯器不支持它 – nux

+0

也許是舊的。小型(標準)和中型代碼模型被提前添加,大型模型出現較晚。 – hirschhornsalz

+0

我非常感謝你,這看起來非常好,明確地解決絕對尋址問題 – nux

2

你問的是可行的,但不是很容易。

這樣做的一種方法是補償代碼在其指令中的移動。您需要查找所有使用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累加器指令。

+0

感謝您的回答。據我所知,這種方法是在運行時重新計算movs的地址值。這看起來很重要的計劃,所以如果這是我會考慮一個不同的實施 – nux