37

変更が重要でない場合でも、継続的に変更されるコードを記述したいと思います。

たとえば、次のようなものかもしれません

for i in 1 to  100, do 
begin
   x := 200
   for j in 200 downto 1, do
    begin
       do something
    end
end

コードで、最初の反復後に行x := 200を別の行にx := 199変更し、次の反復後に次のx := 198ように変更する必要があるとします。

そのようなコードを書くことは可能ですか?そのためにインラインアセンブリを使用する必要がありますか?

編集:これが私がCでそれをやりたい理由です:

このプログラムは実験的なオペレーティングシステムで実行され、他の言語からコンパイルされたプログラムの使用方法がわかりません。このようなコードが必要な本当の理由は、このコードが仮想マシンのゲストオペレーティングシステムで実行されているためです。ハイパーバイザーは、コードのチャンクを変換するバイナリトランスレーターです。翻訳者はいくつかの最適化を行います。コードのチャンクを1回だけ変換します。次回ゲストで同じチャンクが使用されるとき、トランスレータは以前に変換された結果を使用します。これで、コードがその場で変更された場合、翻訳者はそれに気づき、以前の翻訳を失効としてマークします。したがって、同じコードの再翻訳を強制します。これは私が達成したいことであり、翻訳者に多くの翻訳を強制することです。通常、これらのチャンクは、分岐命令(ジャンプ命令など)間の命令です。自己修正コードはこれを達成するための素晴らしい方法だと思います。

4

11 に答える 11

15

独自の自己変更コードを作成できる C で仮想マシンを作成することを検討することをお勧めします。

自己変更実行可能ファイルを作成したい場合は、対象となるオペレーティング システムに大きく依存します。インメモリ プログラム イメージを変更することで、目的のソリューションに近づくことができます。これを行うには、プログラムのコード バイトのメモリ内アドレスを取得します。次に、このメモリ範囲でオペレーティング システムの保護を操作して、アクセス違反や '''SIG_SEGV''' に遭遇することなくバイトを変更できるようにします。最後に、ポインター (おそらく '''unsigned char *''' ポインター、おそらく RISC マシンの場合は '''unsigned long *''') を使用して、コンパイルされたプログラムのオペコードを変更します。

重要な点は、ターゲット アーキテクチャのマシン コードを変更することです。実行中の C コードの標準的な形式はありません。C は、コンパイラへのテキスト入力ファイルの仕様です。

于 2011-09-16T15:54:40.087 に答える
9

可能ですが、おそらく移植可能ではなく、実行中のコードの読み取り専用メモリセグメントや、OS によって配置されたその他の障害と競合する必要がある場合があります。

于 2011-09-16T15:44:00.027 に答える
5

これは良いスタートになるでしょう。基本的にCのLisp機能:

http://nakkaya.com/2010/08/24/a-micro-manual-for-lisp-implemented-in-c/

于 2011-09-16T15:58:05.383 に答える
5

必要な自由度によっては、関数ポインターを使用して目的を達成できる場合があります。x疑似コードを出発点として使用し、ループ インデックスの変更に応じて変数をさまざまな方法で変更する場合を考えてみiます。次のようなことができます。

#include <stdio.h>

void multiply_x (int * x, int multiplier)
{
    *x *= multiplier;
}

void add_to_x (int * x, int increment)
{
    *x += increment;
}

int main (void)
{
    int x = 0;
    int i;

    void (*fp)(int *, int);

    for (i = 1; i < 6; ++i) {
            fp = (i % 2) ? add_to_x : multiply_x;

            fp(&x, i);

            printf("%d\n", x);
    }

    return 0;
}

プログラムをコンパイルして実行すると、出力は次のようになります。

1
2
5
20
25

x明らかに、これは実行ごとにやりたいことが有限数ある場合にのみ機能します。変更を永続的にするには (これは「自己変更」から必要なものの一部です)、関数ポインター変数をグローバルまたは静的にする必要があります。多くの場合、この種のことを達成するためのより単純で明確な方法があるため、このアプローチを本当にお勧めできるかどうかはわかりません。

于 2011-09-16T16:03:28.837 に答える
4

そのためには、自己解釈言語 (C のようにハードコンパイルおよびリンクされていない) の方が適している場合があります。Perl、javascript、PHP には、eval()あなたの目的に合うかもしれない邪悪な機能があります。これにより、常に変更してから を介して実行する一連のコードを作成できますeval()

于 2011-09-16T15:34:41.167 に答える
3

C で LISP を実装し、それを使用することについての提案は、移植性の懸念から確かなものです。しかし、あなたが本当に望むなら、プログラムのバイトコードをメモリにロードしてからメモリに戻すことで、多くのシステムでこれを逆方向に実装することもできます。

これを行うには、いくつかの方法があります。1 つの方法は、バッファ オーバーフローの悪用によるものです。もう 1 つの方法は、mprotect() を使用してコード セクションを書き込み可能にし、コンパイラで作成された関数を変更することです。

このような手法は、プログラミングの課題や難読化された競争にとっては楽しいものですが、C が未定義の動作と見なすものを悪用しているという事実と、コードがどれほど読めないかを考えると、運用環境では避けるのが最善です。

于 2013-05-07T03:29:19.930 に答える
1

これは、C ++を使用してWindowsで行う方法です。読み取り/書き込み保護でバイト配列を VirtualAlloc し、そこにコードをコピーし、読み取り/実行保護で VirtualProtect する必要があります。何もせずに返す関数を動的に作成する方法を次に示します。

#include <cstdio>
#include <Memoryapi.h>
#include <windows.h>
using namespace std;
typedef unsigned char byte;

int main(int argc, char** argv){
    byte bytes [] = { 0x48, 0x31, 0xC0, 0x48, 0x83, 0xC0, 0x0F, 0xC3 }; //put code here
    //xor %rax, %rax
    //add %rax, 15
    //ret
    int size = sizeof(bytes);
    DWORD protect = PAGE_READWRITE;
    void* meth = VirtualAlloc(NULL, size, MEM_COMMIT, protect);
    byte* write = (byte*) meth;
    for(int i = 0; i < size; i++){
        write[i] = bytes[i];
    }
    if(VirtualProtect(meth, size, PAGE_EXECUTE_READ, &protect)){
        typedef int (*fptr)();
        fptr my_fptr = reinterpret_cast<fptr>(reinterpret_cast<long>(meth));
        int number = my_fptr();
        for(int i = 0; i < number; i++){
            printf("I will say this 15 times!\n");
        }
        return 0;
    } else{
        printf("Unable to VirtualProtect code with execute protection!\n");
        return 1;
    }
}

このツールを使用してコードを組み立てます。

于 2020-09-06T06:07:07.777 に答える