自己変更コードの実際の用途はありますか?
それらがワーム/ウイルスの作成に使用できることは知っていますが、プログラマーが自己変更コードを使用しなければならない正当な理由があるかどうか疑問に思っていました.
何か案は?仮定のシチュエーションも大歓迎です。
自己変更コードの実際の用途はありますか?
それらがワーム/ウイルスの作成に使用できることは知っていますが、プログラマーが自己変更コードを使用しなければならない正当な理由があるかどうか疑問に思っていました.
何か案は?仮定のシチュエーションも大歓迎です。
「自己変更コード」に関するウィキペディアのエントリには、すばらしいリストがあります。
- 状態依存ループの半自動最適化。
- 実行時コード生成、または特定の呼び出しで記述されたキー比較を実行するためのコードを準備する一般的な並べ替えユーティリティなど、実行時または読み込み時のアルゴリズムの特殊化 (リアルタイム グラフィックスのドメインなどで一般的)。
- オブジェクトのインライン状態の変更、またはクロージャーの高レベルの構築のシミュレーション。
- 通常は動的ライブラリのロード時に行われるサブルーチン アドレス呼び出しのパッチ、または呼び出しごとに、実際のアドレスを使用するためにサブルーチンのパラメータへの内部参照にパッチを適用します。これが「自己変更コード」と見なされるかどうかは、用語の問題です。
- 遺伝的プログラミングなどの進化的コンピューティング システム。
- 逆アセンブラやデバッガを使用するなどして、コードを隠してリバース エンジニアリングを防止する。
- ウイルス/スパイウェアスキャンソフトウェアなどによる検出を回避するためのコードの隠蔽。
- すべてのプログラムとデータを消去するため、またはハードウェアをバーンインするために、繰り返しオペコードのローリング パターンで (一部のアーキテクチャでは) メモリを 100% 埋めます。
- メモリやディスク容量が限られている場合など、実行時に解凍して実行するコードの圧縮。
- 一部の非常に限られた命令セットでは、特定の機能を実現するために自己変更コードを使用する以外に選択肢がありません。たとえば、負の場合は減算と分岐の「命令」のみを使用する「1 命令セット コンピュータ」マシンは、間接コピーを実行できません (C プログラミングの「*a = **b」に相当するようなもの)。言語) 自己変更コードを使用せずに。
- フォールト トレランスのための命令の変更
自己変更コードを使用してハッカーを阻止することについて:
数回のファームウェア更新の過程で、DirectTV はゆっくりとプログラムをスマート カードに組み込み、ハッキングされたカードを破壊して、無給のチャンネルを違法に受信しました。詳細については、 Black Sunday Hackに関する Jeff の Coding Horror の記事を参照してください。
自己変更コードが次の目的で使用されているのを見てきました。
プログラムがその場でそれ自体のコードをさらに書くことによる速度の最適化
リバースエンジニアリングをより困難にするための難読化
RAM が制限されていた以前は、メモリを節約するために自己変更コードが使用されていました。最近では、たとえば、UPXなどのアプリケーション圧縮ユーティリティを使用して、アプリケーションの圧縮イメージをロードした後、独自のコードを解凍/変更します。
コモドール64には多くのレジスタがなく、1Mhzプロセッサを搭載しているためです。値だけオフセットされたメモリアドレスを読み取る必要がある場合は、ソースを変更する方が簡単です。
@Reader:
LDA $C000
STA $D020
INC Reader+1
JMP Reader
とにかく自己修正コードを書いたのはこれが最後です:-)
人工知能?
1960 年代のアセンブリ言語は、自己変更コードを使用して、スタックなしで関数呼び出しを実装していました。
クヌート、v1、1ed p.182:
MAX100 STJ EXIT ;Subroutine linkage
ENT3 100 ;M1. Initialize
JMP 2F
1H CMPA X,3 ;M3. Compare
JGE *+3
2H ENT2 0,3 ;M4. Change m
LDA X,3 ;(New maximum found)
DEC3 1 ;M5. Decrease k
J3P 1B ;M2. All tested?
EXIT JMP * ;Return to main program
このコーディングをサブルーチンとして含む大規模なプログラムでは、単一の命令「JMP MAX100」によって、レジスタ A が位置 X + 1 から X + 100 までの現在の最大値に設定され、最大値の位置が rI2 に表示されます。 . この場合のサブルーチン・リンケージは、命令「MAX100 STJ EXIT」と、その後の「EXIT JMP *」によって実現されます。J レジスタの動作方法により、終了命令は、MAX100 への元の参照が行われた場所の次の場所にジャンプします。
編集:ここに簡単な説明があっても、何が起こっているのかわかりにくいかもしれません。行MAX100 STJ EXIT
のMAX100
は、命令 (したがってプロシージャ全体) のラベルでありSTJ
、ジャンプ レジスタの STORE を意味します (ここから来た場所)、EXIT
'EXIT' とラベル付けされたメモリ位置が STORE のターゲットであることを意味します。EXIT
、後でわかりますが、最後の命令のラベルです。つまり、コードを上書きしています!ただし、多くの命令 (STJ
ここを含む) は、命令語のオペランド部分のみを暗黙的に上書きします。したがって、JMP
はそのまま残り、*
はダミーのトークンです。そこに配置する意味は実際には何もないため、上書きされるだけです。
自己変更コードは、レジスター間接アドレッシングが利用できない場合にも使用されますが、必要なアドレスはレジスターのすぐそこにあります。PDP-1 LISP:
dap .+1 ;deposit address part of accumulator in (IP+1)
lac xy ;load accumulator with (ADDRESS) [xy is a dummy symbol, just like * above]
これら 2 つの命令ACC := (ACC)
は、ロード命令のオペランドを変更することによって実行されます。
このような変更は比較的安全で、アンティークのアーキテクチャでは必要です。
それは本当に本当にクールで、時にはそれだけで十分な理由だからです。
多くの理由。私の頭の上から:
ランタイム クラスの構築とメタ プログラミング。たとえば、SQL テーブルへの接続を取得し、そのテーブルに特化したクライアント クラスを生成するクラス ファクトリがあるとします (列のアクセサー、find メソッドなどを使用)。
それからもちろん、有名な bitblt の例と正規表現の類似物があります。
JIT をトレースする RT 情報に基づいて動的に最適化する
付加的環境における ada スタイルのジェネリック関数のサブタイプの特殊化。
-- マーカスQ
動的リンクは、一種の自己変更 (絶対および/または相対ジャンプ位置にパッチを当てる) です ... ただし、これは通常、O/S のプログラム ローダーによって行われます。
ニューラル ネットワークは一種の自己変更コードです。
次に、自分自身を変更する進化的アルゴリズムがあります。
LOL - 私は 2 つの機会に自己変更コードを書きました:
自己変更コードが代替手段よりも効率的であるシナリオがあるかもしれないと想像できますが、明らかなことは何もありません。一般に、これは避けるべきものです (悪夢のようなデバッグなど)。ただし、上記のように意図的に難読化しようとしている場合を除きます。
Mike Abrash は、しばらく前に Dr. Dobb's Journal の Pixomatic コード ジェネレーターについて説明しました: http://www.ddj.com/architect/184405807。これは、ソフトウェア 3d dx7(?) 互換のラスタライザーです。
独自のスクリプト言語を実装するアプリケーションは、多くの場合これを行います。たとえば、データベース サーバーは多くの場合、この方法でストアド プロシージャ (またはクエリ) をコンパイルします。
SwiftShader での動的コード生成は、CPU に Direct3D 9 を効率的に実装できる自己修正コードの形式です。