2013-08-19 28 views
0

假設我想執行任意的mov指令。我可以寫下面的函數(使用GCC內聯彙編):僅使用現有代碼執行任意代碼

void mov_value_to_eax() 
{ 
    asm volatile("movl %0, %%eax"::"m"(function_parameter):"%eax"); 
    // will move the value of the variable function_parameter to register eax 
} 

我可以使功能像這樣的,將在每一個可能的登記工作。 我的意思是 -

void movl_value_to_ebx() { asm volatile("movl %0, %%ebx"::"m"(function_parameter):"%ebx"); } 
void movl_value_to_ecx() { asm volatile("movl %0, %%ecx"::"m"(function_parameter):"%ecx"); } 
... 

以類似的方式,我可以在內存中寫入功能,將在任意地址的內存移動到特定的寄存器和專用寄存器任意地址。 (mov eax, [memory_address]mov [memory_address],eax)

現在,我可以執行這些基本指令,只要我想要,所以我可以創建其他指令。例如,一個寄存器移到另一個寄存器:

function_parameter = 0x028FC; 
mov_eax_to_memory(); // parameter is a pointer to some temporary memory address 
mov_memory_to_ebx(); // same parameter 

所以我可以解析彙編指令,並決定使用基於它有什麼功能,像這樣:

if (sourceRegister == ECX) mov_ecx_to_memory(); 
if (sourceRegister == EAX) mov_eax_to_memory(); 
... 
if (destRegister == EBX) mov_memory_to_ebx(); 
if (destRegister == EDX) mov_memory_to_edx(); 
... 

如果它可以工作,它允許你執行任意的mov指令。

另一種方法是創建一個函數列表來調用,然後遍歷列表並調用每個函數。也許它需要更多的技巧來製作像這樣的等效指令。

所以我的問題是這樣的:是可以爲所有(或某些)可能的操作碼做這樣的事情嗎?它可能需要很多函數來編寫,但是有可能創建一個解析器,它將基於給定的彙編指令以某種方式構建代碼,並執行它,否則這是不可能的?

編輯:您不能更改內存保護或寫入可執行的內存位置。

+1

這類問題引發了一個問題:「爲什麼要做這樣的事情?」 – phonetagger

+0

因爲如果它可以工作,它可以是一種執行代碼的方法,如果你不能將它寫入內存。 (操作系統可能會阻止下載和執行代碼) – Tomer

回答

3

我真的不清楚你爲什麼問這個問題。首先,這個函數......

void mov_value_to_eax() 
{ 
    asm volatile("movl %0, %%eax"::"m"(function_parameter):"%eax"); 
    // will move the value of the variable function_parameter to register eax 
} 

...使用GCC內聯彙編,但功能本身並不在線,這意味着會有序幕&尾聲代碼包裝它,這可能會影響你的意結果。您可以改爲使用GCC內聯彙編函數(而不是包含GCC內聯彙編的函數),這可能會使您更接近您想要的內容,但仍存在問題.....

好的,所以假設你爲每一個可能的x86操作碼(至少是GCC彙編器知道的)編寫一個GCC內聯彙編函數。現在假設你想以任意順序調用這些函數來完成你想要完成的任何事情(考慮哪些操作碼在環3(或者在你編碼的任何環中)是合法的)。您的示例顯示您使用C語句編碼邏輯,以確定是否調用內聯彙編函數。猜猜看:那些C語句正在使用處理器寄存器(甚至可能是EAX!)來完成它們的任務。無論您想通過調用這些任意的內聯彙編函數來做什麼,都會被編譯器發出的邏輯彙編代碼(if (...)等)所壓制。反之亦然:你的內聯彙編函數任意指令都在寄存器上跺腳,編譯器發出的指令不希望被踩下。結果不可能在沒有崩潰的情況下運行。

如果你想在程序集中編寫代碼,我建議你只需在程序集&中使用GCC彙編器來組裝它。或者,您可以在asm()語句中編寫完整的C可調用匯編函數,並根據您的C代碼調用它們(如果您喜歡)。但是您編寫的C可調用匯編函數需要在您使用的調用約定(ABI)的規則內進行操作:如果您的彙編函數使用被調用者保存的寄存器,則您的函數需要將原始值保存在該寄存器中(通常在堆棧上),然後在返回給調用者之前將其恢復。

+0

我知道。你必須保存並恢復一些寄存器。我只是想知道它是否可以這樣工作。 – Tomer

+0

@Tomer通過這種方式「工作」你知道什麼?它會爲你製作不混亂的操作碼嗎?很可能不會,即使它會 - 這個操作碼不會是最優的。 –

+0

我的意思是它會執行給定的代碼。這並不重要。如果代碼顯示消息 - 它會顯示一條消息。 – Tomer

1

... OK,根據您的評論Because if it's working it can be a way to execute code if you can't write it to memory. (the OS may prevent it) ....

當然,你可以執行任意指令中(只要他們是合法的,無論環你正在運行中)。 JIT將如何工作?你只需要調用OS系統調用來設置你的指令所在的內存頁面的權限......將它們改爲「可執行」,然後調用'em!

+0

但您無法更改內存保護。這正是這個想法。 – Tomer

+0

@Tomer - 您的文章沒有提及您使用的操作系統,但由於您使用的是GCC,因此我認爲您使用的是Linux。在這種情況下,只需分配(通過'new'或'malloc()')一個適當大小的緩衝區,並在其中寫入指令。使用系統調用接口'mprotect()'來改變你的緩衝區的權限來讀取+寫入+執行,然後你可以通過將入口地址作爲指向函數的指針來運行你的指令,然後解除引用指針(「call它」)。當然,如果你希望它返回,你必須在你的asm代碼中觀察你的ABI的函數調用規則。 – phonetagger

+0

@Tomer - ...「適當大小的緩衝區」:您會希望緩衝區以頁面邊界開始和結束。確保這一點的一種技術是分配比您需要的更多(通過PAGE_SIZE-1字節),然後將返回的指針對齊到下一個頁面邊界。如果你看一下'mprotect()'手冊頁,它可能給出了一個如何完成這個過程的例子。 – phonetagger