2013-08-19 32 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