私はMSILを調べて、多くの nop の指示があることに気付いています。 MSDNの記事によると、彼らは何のアクションも取らず、オペコードにパッチが適用された場合にスペースを埋めるために使用されます。リリースビルドよりもデバッグビルドで多く使用されています。これらの種類のステートメントがアセンブリ言語で使用され、オペコードがWordの境界に適合することを確認していますが、MSILで必要なのはなぜですか?
NOPにはいくつかの目的があります。
デバッグでnopsが使用される方法は次のとおりです。
Nopsは、言語コンパイラ(C#、VBなど)によって暗黙的なシーケンスポイントを定義するために使用されます。これらは、JITコンパイラーに、マシン命令をIL命令にマップし直すことができる場所を指示します。
DebuggingModes.IgnoreSymbolStoreSequencePoints に関するRick Byerのブログエントリは、いくつかの詳細を説明しています。
また、C#はコール指示の後にNopsを配置するため、ソース内のリターンサイトの場所は、コール後の行ではなくコールアウトになります。
これは、リリースビルドが何も出力しないコードの行ベースのマーカー(ブレークポイントなど)の機会を提供します。
また、特定のプロセッサまたはアーキテクチャ向けに最適化する場合、コードの実行を高速化する可能性があります。
長い間、プロセッサはほぼ並行して動作する複数のパイプラインを採用しているため、2つの独立した命令を同時に実行できます。 2つのパイプラインを持つ単純なプロセッサでは、最初の命令がすべての命令をサポートし、2番目の命令がサブセットのみをサポートします。また、まだ完了していない前の命令の結果を待つ必要がある場合、パイプライン間にいくつかのストールがあります。
これらの状況では、専用のnopは次の命令を特定のパイプライン(最初の、または最初ではない)に強制し、次の命令のペアを改善してnopは償却以上です。
最近(4年間)働いていた1つのプロセッサで、次の操作が開始される前に前の操作が終了したことを確認するためにNOPが使用されました。例えば:
レジスタに値をロード(8サイクルかかります)nop 8レジスタに1を追加
これにより、追加操作の前にレジスタの値が正しいことが確認されました。
別の用途は、vector0のアドレスがたとえば0、vector 1 0x20などであるため特定のサイズ(32バイト)である必要がある割り込みベクトルなどの実行単位を埋めることでした。必要です。
おい!ノーオペレーションは素晴らしいです!時間を消費するだけの命令です。薄暗い暗黒時代では、重要なループでタイミングを微調整したり、さらに重要なことに自己修正コードのフィラーとして使用します。
Nopは、バッファオーバーフローエクスプロイトのペイロードに不可欠です。
Ddaaが言ったように、nopsを使用するとスタック内の分散を考慮することができるため、戻りアドレスを上書きすると、nopスレッド(多数のnopsが連続して)にジャンプしてから、実行コードにジャンプするのではなく、正しく実行されます命令の先頭ではないバイト。
50年も遅すぎるけどね。
Nopは、アセンブリコードを手で入力する場合に便利です。コードを削除する必要がある場合は、古いオペコードをnopできます。
同様に、オペコードを上書きして新しいコードを挿入し、別の場所にジャンプすることもできます。そこで、上書きされたオペコードを配置し、新しいコードを挿入します。準備ができたら、元に戻ります。
時には、利用可能なツールを使用する必要がありました。場合によっては、これは非常に基本的なマシンコードエディタにすぎません。
最近のコンパイラでは、この手法はまったく意味をなしません。
それらの古典的な使用法の1つは、デバッガーが常にソースコード行をIL命令に関連付けることができるようにすることです。
デバッグ中にedit-and-continueをサポートするために使用できます。デバッガーに、オフセットなどを変更せずに古いコードを新しいコードに置き換える作業の余地を提供します。
また、コード内のNOPを見て、プレースホルダーとしての機能を難読化するように自身を変更しています(非常に古いコピー防止)。
やや非正統的な使用法は NOP-Slides で、バッファオーバーフローエクスプロイトで使用されます。
ソフトウェアクラッキングシーンでは、アプリケーションのロックを解除する古典的な方法は、キー、登録、または期間などをチェックする行にNOPでパッチを適用することです。 。
NOPを使用して、ISRに入った後に蓄積された遅延を自動的に調整しました。タイミングを釘付けにするのに非常に便利です。
リンカは、長い命令(通常は長いジャンプ)を短い命令(短いジャンプ)に置き換えることができます。 NOPは余分なスペースを取ります-他のジャンプが機能しなくなるため、コードを移動できませんでした。これはリンク時に発生するため、コンパイラはロングジャンプとショートジャンプのどちらが適切かを判断できません。
少なくとも、それは彼らの伝統的な用途の一つです。
これは特定の質問に対する答えではありませんが、昔はNOPを使用して ブランチ遅延スロット を埋めることができました。 。
.NETコンパイラはMSIL出力を調整しますか? ILへのアクセスを高速化するのに役立つかもしれないと思います...また、他のハードウェアプラットフォームでは、移植可能に調整されたアクセスが必要なことも理解しています。
私が学んだ最初のアセンブリはSPARCだったので、別の命令、通常は分岐命令の上に置く命令で埋めることができない場合、分岐遅延スロットに精通しています。またはループでカウンターをインクリメントする場合、NOPを使用します。
私はクラッキングに慣れていませんが、NOPを使用してスタックを上書きするのが一般的であるため、悪意のある機能がどこから始まるかを正確に計算する必要はありません。