1

任意の 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 命令を実行できます。

別のオプションは、呼び出す関数のリストを作成し、リストをループして各関数を呼び出すことです。おそらく、これらのような同等の命令を作成するには、さらにトリックが必要になるでしょう。

だから私の質問はこれです:可能なオペコードのすべて(または一部)に対してそのようなことを行うことは可能ですか? おそらく多くの関数を記述する必要がありますが、与えられたアセンブリ命令に基づいて何らかの方法でコードを構築し、それを実行するパーサーを作成することは可能ですか、それとも不可能ですか?

EDIT : メモリ保護を変更したり、実行可能なメモリの場所に書き込んだりすることはできません。

4

2 に答える 2

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 アセンブラを使用してアセンブルすることをお勧めします。または、必要に応じて、C 呼び出し可能なアセンブリ関数全体をasm()ステートメント内に記述し、それらを C コードから呼び出すこともできます。ただし、作成する C 呼び出し可能なアセンブリ関数は、使用している呼び出し規約 (ABI) の規則内で動作する必要があります。アセンブリ関数が呼び出し先保存レジスタを使用する場合、関数は元の値をそのレジスタに保存する必要があります。 (通常はスタック上)、呼び出し元に戻る前に復元します。

于 2013-08-19T18:28:50.000 に答える
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システムコールを呼び出す必要があるだけです...それらを「実行可能」に変更してから呼び出します!

于 2013-08-19T18:32:57.047 に答える