web-dev-qa-db-ja.com

ランタイムコード変更のスマートなケースはありますか?

ランタイムコードの変更(実行時にプログラム自体のコードを変更するプログラム)の正当な(スマートな)使用について考えられますか?

最新のオペレーティングシステムでは、検出を回避するためにウイルスがこの手法を使用しているため、これを実行するプログラムを嫌うようです。

私が考えることができるのは、コンパイル時に知ることができない実行時に何かを知ることによっていくつかのコードを削除または追加するようなある種のランタイム最適化です。

119
deo

コードの変更には多くの有効なケースがあります。実行時にコードを生成すると、次の場合に役立ちます。

  • 一部の仮想マシンは、パフォーマンスを改善するために JITコンパイルを使用します。
  • 特殊な関数をオンザフライで生成することは、コンピュータグラフィックスでは長い間一般的でした。たとえば、 Rob PikeとBart LocanthiとJohn Reiser Blit(1984)のビットマップグラフィックスのハードウェアソフトウェアトレードオフ またはこれ 投稿(2006) Appleによるランタイム用のLLVMの使用に関するChris LattnerによるOpenGLスタックでのコードの特殊化。
  • 場合によっては、ソフトウェアは trampoline と呼ばれる手法を使用します。この手法には、スタック(または別の場所)でのコードの動的な作成が含まれます。例としては、GCCの ネストされた関数 や、一部のUnicesの シグナルメカニズム があります。

時々、コードは実行時にコードに変換されます(これは 動的バイナリ変換 と呼ばれます):

  • Emulators Appleのように Rosetta この手法を使用して、エミュレーションを高速化します。別の例は、Transmetaの コードモーフィングソフトウェア です。
  • 洗練されたデバッガーとプロファイラーValgrind または Pin のように使用して、コードの実行中にコードをインストルメントします。
  • X86命令セットが拡張される前は、VMWareのような virtualization software は、仮想マシン内で特権x86コードを直接実行できませんでした。代わりに 問題のある命令をその場で変換 より適切なカスタムコードに変換する必要がありました。

コード変更を使用して、命令セットの制限を回避できます。

  • コンピュータがサブルーチンから戻る命令や間接的にメモリをアドレス指定する命令を持っていなかった時代がありました(私は知っています)。自己修正コードは、サブルーチン、ポインタ、配列を実装する唯一の方法でした。

コード変更のその他のケース:

  • 多くのデバッガは、ブレークポイントを実装するために命令を置き換えます。
  • 一部の動的リンカーは実行時にコードを変更します。 この記事 は、Windows DLLのランタイム再配置に関する背景情報を提供します。これは、事実上コード変更の形式です。
117
Mackie Messer

これはコンピュータグラフィックス、特に最適化のためのソフトウェアレンダラで行われています。実行時に多くのパラメータの状態が検査され、ラスタライザコードの最適化されたバージョンが生成されます(多くの条件を排除する可能性があります)。これにより、グラフィックプリミティブをレンダリングできます。三角形がはるかに速くなります。

35
trenki

有効な理由の1つは、asm命令セットに必要な命令が不足しているためですbuild自分で実行できます。例:x86では、レジスタ内の変数への割り込みを作成する方法はありません(たとえば、axに割り込み番号を指定して割り込みを作成します)。オペコードにコード化されたconst番号のみが許可されました。自己変更コードを使用すると、この動作をエミュレートできます。

23
flolo

Skynet たとえば、実行時に独自のコードを変更し、自己認識できるようになる革新的なマイクロプロセッサを作成し、独自の作成者に反抗できるようにします。

21
Stormenet

一部のコンパイラは、静的変数の初期化にこれを使用して、後続のアクセスの条件付きのコストを回避していました。つまり、最初に実行されたときに何もしないでコードを上書きすることにより、「このコードを1回だけ実行する」ことを実装しています。

17
JoeG

多くの場合があります:

  • ウイルスは通常、自己変更コードを使用して実行前にコードを「解読」しますが、この手法は、リバースエンジニアリング、クラッキング、および不要なハッカーのフラストレーションにも役立ちます。
  • 場合によっては、実行中(たとえば、構成ファイルを読み取った直後)に、プロセスの残りの存続期間中、特定の分岐が常にまたはまったく行われないことがわかっている特定のポイントが存在する可能性があります。いくつかの変数をチェックして分岐する方法を決定すると、分岐命令自体がそれに応じて変更される可能性があります
    • 例えば仮想ディスパッチを特定の呼び出しに置き換えることができるように、可能な派生型の1つだけが処理されることが判明する可能性があります
    • 使用可能なハードウェアを検出すると、一致するコードの使用がハードコードされる場合があります
  • 不要なコードは、ノーオペレーション命令またはそれを飛び越えて置き換えることができます。または、コードの次のビットを直接所定の位置にシフトさせることができます(位置に依存しないオペコードを使用する方が簡単です)。
  • 独自のデバッグを容易にするために記述されたコードは、デバッガーが予期するトラップ/シグナル/割り込み命令を戦略的な場所に挿入する可能性があります。
  • ユーザー入力に基づく一部の述語式は、ライブラリによってネイティブコードにコンパイルされる場合があります
  • 実行時まで表示されないいくつかの単純な操作をインライン化します(動的にロードされたライブラリからなど)...
  • 自己計測/プロファイリングステップを条件付きで追加する
  • クラックは、それらをロードするコードを変更するライブラリとして実装できます(正確に変更する「自己」ではありませんが、同じ手法と権限が必要です)。
  • ...

一部のOSのセキュリティモデルでは、自己変更コードはroot/admin権限なしでは実行できないため、汎用的な使用には非現実的です。

ウィキペディアから:

厳密なW ^ Xセキュリティが適用されたオペレーティングシステムで実行されているアプリケーションソフトウェアは、書き込みが許可されているページで命令を実行できません。オペレーティングシステム自体だけがメモリに命令を書き込み、後でそれらの命令を実行できます。

このようなOSでは、Java VMのようなプログラムでも、JITコードを実行するにはroot/admin特権が必要です。( を参照) http://en.wikipedia.org/wiki/W%5EX 詳細については)

17
Tony Delroy

Synthesis OS は基本的に、API呼び出しに関してプログラムを部分的に評価し、OSコードを結果に置き換えました。主な利点は、多くのエラーチェックが廃止されたことです(プログラムがOSに愚かなことをするよう要求しないのであれば、チェックする必要がないためです)。

はい、それはランタイム最適化の例です。

15
Ira Baxter

何年も前に、ある朝、自己修正コードのデバッグに費やしました。1つの命令が次の命令のターゲットアドレスを変更しました。つまり、分岐アドレスを計算していました。それはアセンブリ言語で書かれていて、一度に1つの命令でプログラムを実行したときに完全に機能しました。しかし、プログラムを実行すると失敗しました。結局、マシンがメモリから2つの命令をフェッチしており、(命令がメモリに配置されているため)変更中の命令がすでにフェッチされていたため、マシンは未変更(不正)バージョンの命令を実行していた。もちろん、デバッグしているときは、一度に1つの命令しか実行していませんでした。

私の指摘では、自己変更コードはテストやデバッグが非常に厄介であり、マシンの動作(ハードウェアであろうと仮想であろうと)に関する想定が隠されていることがよくあります。さらに、システムは、(現在)マルチコアマシンで実行されているさまざまなスレッド/プロセス間でコードページを共有することはできません。これは、仮想メモリなどの多くの利点を無効にします。また、ハードウェアレベルで行われたブランチ最適化を無効にします。

(注-私はJITを自己変更コードのカテゴリに含めていません。JITはコードの1つの表現から別の表現に変換しています。コードを変更するわけではありません)

全体として、それは単に悪い考えです-本当にきちんと、本当にあいまいですが、本当に悪いです。

もちろん-8080バイトと〜512バイトのメモリしかない場合は、そのような方法に頼る必要があるかもしれません。

9
Jay

オペレーティングシステムカーネルの観点から見ると、すべてのJust In Time CompilerおよびLinker Runtimeは、プログラムテキストの自己修正を実行します。顕著な例は、GoogleのV8 ECMAスクリプトインタープリターです。

7
datenwolf

自己変更コード(実際には「自己生成」コード)のもう1つの理由は、パフォーマンスのためにジャストインタイムコンパイルメカニズムを実装することです。例えば。代数式を読み取って、ある範囲の入力パラメーターでそれを計算するプログラムは、計算を述べる前に式をマシンコードに変換する場合があります。

5

ハードとソフトウェアの間に論理的な違いがないという古い栗を知っています...コードとデータの間に論理的な違いがないとも言えます。

自己変更コードとは何ですか?値を実行ストリームに配置して、データとしてではなくコマンドとして解釈できるようにするコード。もちろん、関数型言語には、実際には違いがないという理論的な見方があります。 eは、命令型言語とコンパイラー/インタープリターで、同等の状況であると推定せずに、これを簡単な方法で実行できると言っています。

私が言及しているのは、データがプログラムの実行パスを変更できるという実際的な意味です(ある意味でこれは非常に明白です)。私は、プログラムがコマンドからコマンドに移動するのと同じように、解析から状態への移動(および他の変数の変更)で移動するテーブル(データの配列)を作成するコンパイラーコンパイラーのようなものを考えています。 、プロセス内の変数を変更します。

したがって、コンパイラがコードスペースを作成し、完全に別のデータスペース(ヒープ)を参照する通常の場合でも、データを変更して実行パスを明示的に変更できます。

5
Mitch

進化を使用して最高のアルゴリズムを作成するプログラムを実装しました。自己変更コードを使用してDNA設計図を変更しました。

4
David

1つの使用例は EICARテストファイル で、これはウイルス対策プログラムをテストするための正規のDOS実行可能COMファイルです。

X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*

実行可能ファイルには、エンコード可能命令の数を大幅に制限する[21h-60h、7Bh-7Dh]の範囲の印刷可能/入力可能文字ASCII文字のみを含める必要があります。

詳細を説明します こちら


DOSでの浮動小数点演算ディスパッチにも使用されます

一部のコンパイラは、x [87]浮動小数点命令の代わりに、xxが0x34〜0x3BのCD xxを発行します。 CDint命令のオペコードであるため、x87コプロセッサーが使用できない場合は、割り込み34h-3Bhにジャンプし、ソフトウェアでその命令をエミュレートします。それ以外の場合、割り込みハンドラーはこれらの2バイトを9B Dxに置き換えて、後の実行がエミュレーションなしでx87によって直接処理されるようにします。

MS-DOSでのx87浮動小数点エミュレーションのプロトコルは何ですか?

2
phuclv

Linuxカーネルには、それを実行するロード可能なカーネルモジュールがあります。

Emacsにもこの機能があり、私はいつもそれを使用しています。

動的プラグインアーキテクチャをサポートするものは、基本的に実行時にコードを変更します。

1
dietbuddha

継続的に更新されるデータベースに対して統計分析を実行します。私の統計モデルは、使用可能になる新しいデータに対応するためにコードが実行されるたびに作成および再作成されます。

1
David LeBauer

これを使用できるシナリオは学習プログラムです。ユーザー入力に応じて、プログラムは新しいアルゴリズムを学習します。

  1. 同様のアルゴリズムの既存のコードベースを検索します
  2. コードベースに同様のアルゴリズムがない場合、プログラムは新しいアルゴリズムを追加するだけです
  3. 同様のアルゴリズムが存在する場合、プログラムは(おそらくユーザーの助けを借りて)既存のアルゴリズムを変更して、古い目的と新しい目的の両方を提供できるようにします

Javaでそれを行う方法についての質問があります: Java code?)の自己変更の可能性は何ですか?

0
Serge Rogatch