CIL nopオペコードの目的は何ですか?


82

私はMSILを調べていますが、MSILには多くのnop命令があることに気づきました。

MSDNの記事によると、これらはアクションを実行せず、オペコードにパッチが適用されている場合はスペースを埋めるために使用されます。これらは、リリースビルドよりもデバッグビルドで多く使用されます。

これらの種類のステートメントがアセンブリ言語で使用されて後の命令を調整することは知っていますが、MSILでMSIL nopが必要なのはなぜですか?

(編集者注:受け入れられた回答は、質問が最初に尋ねたMSIL / CIL NOPではなく、マシンコードNOPに関するものです。)


19
言語コンパイラーによってアセンブリーに出力されるMSILnop命令と、アセンブリーの実行時にJITコンパイラーによって出力されるx86 nop命令(そのプラットフォーム上)の間には、これらの回答に大きな混乱があります。[実際、受け入れられた答えはx86 nopsに関するものであり、MSILとは関係ありません。]理想的には、この質問は2つの異なる質問に分割する必要があります。MSIL:: nopの目的?ネイティブプラットフォームnopの目的は?
スティーブシュタイナー

回答:


107

NOPにはいくつかの目的があります。

  • これにより、生成されたコードで他の行と組み合わされている場合でも、デバッガーは行にブレークポイントを配置できます。
  • これにより、ローダーは異なるサイズのターゲットオフセットでジャンプにパッチを適用できます。
  • これにより、コードのブロックを特定の境界に揃えることができ、キャッシュに適しています。
  • これにより、インクリメンタルリンクを使用して、関数全体のサイズ変更を心配することなく、新しいセクションの呼び出しでコードのチャンクを上書きできます。

ウィキペディアから:「NOPは、タイミングの目的、メモリの調整の強制、危険の防止、分岐遅延スロットの占有、ジャンプなどの既存の命令の無効化、または置き換えられるプレースホルダーとして最も一般的に使用されます。プログラム開発の後半でアクティブな命令によって(またはリファクタリングが問題または時間がかかる場合に削除された命令を置き換える)、場合によっては、NOPは軽微な副作用をもたらす可能性があります。たとえば、Motorola68000シリーズのプロセッサではNOP opcodeはパイプラインの同期を引き起こします。」
mbomb007 2017

10

デバッグでMSIL / CIL nops(x86マシンコードではないnop)がどのように使用されるかを次に示します。

Nopsは、言語コンパイラ(C#、VBなど)が暗黙のシーケンスポイントを定義するために使用します。これらは、マシン命令をIL命令にマップして戻すことができる場所をJITコンパイラに通知します。

DebuggingModes.IgnoreSymbolStoreSequencePointsに関するRickByerのブログエントリでは、いくつかの詳細について説明しています。

また、C#は、コールの指示の後にNopsを配置するため、ソース内のリターンサイトの場所は、コール後の回線ではなくコールアウトになります。


また、C#は、コールの指示の後にNopsを配置するため、ソース内のリターンサイトの場所は、コール後の回線ではなくコールアウトになります。 私がこれを手に入れるかどうかわからない。利用できるリファレンスはありますか?
user4922 3819年

8

これは、リリースビルドが何も出力しないコード内の行ベースのマーカー(ブレークポイントなど)の機会を提供します。


ブレークポイントはnopにある必要がありますか?通常のオペコードにブレークポイントを設定しないのはなぜですか?
ダンゴールドスタイン

4
多くの通常のオペコードは、リリースビルドで最適化されます。ブレークポイントがまだ指しているプレースホルダーnopがない限り、ブレークポイントが台無しになります
Jimmy

1
デバッグビルドでは、コードに命令がない場合に中断する命令を提供するためにも使用されます。たとえば、中括弧を開きます。
グレッグD

1
blogs.msdn.com/oldnewthing/archive/2007/08/17/4422794.aspxへの参照をソースとして追加することをお勧めします。
グレッグD

1
さて、これを実際の答えにしてくれたグレッグとジミーに感謝します。
harpo

6

また、特定のプロセッサまたはアーキテクチャ用に最適化する場合、コードの実行が速くなる可能性があります。

プロセッサは長い間、ほぼ並行して動作する複数のパイプラインを採用しているため、2つの独立した命令を同時に実行できます。2つのパイプラインを持つ単純なプロセッサでは、1つ目はすべての命令をサポートし、2つ目はサブセットのみをサポートします。また、まだ終了していない前の命令の結果を待たなければならない場合、パイプライン間にいくつかのストールがあります。

このような状況では、専用のnopが次の命令を特定のパイプライン(最初の、または最初ではない)に強制し、次の命令のペアを改善して、nopのコストが 償却される以上になる場合があります。


5

おい!No-opは素晴らしいです!時間を消費するだけの命令です。薄暗い暗黒時代では、クリティカルループのタイミングで微調整を行うために、またはより重要なことに自己変更コードのフィラーとして使用します。


ハードウェア上で直接実行する場合の使用法は理解していますが、MSILはJITされています。
ダンゴールドスタイン

MSILは、JITを備えたシステムでのみJITされます-MSILはJITを要求しません。
台座

5

私が最近(4年間)働いていた1つのプロセッサーでは、NOPを使用して、次の操作を開始する前に前の操作が終了したことを確認しました。例えば:

登録する値をロードします(8サイクルかかります)nop8レジスタに1を追加します

これにより、追加操作の前にレジスタの値が正しいことが確認されました。

別の使用法は、vector0のアドレスがベクトル1 0x20などの0であるため、特定のサイズ(32バイト)である必要がある割り込みベクトルなどの実行ユニットを入力することでした。必要です。


4

それらを使用して、デバッグ中の編集と続行をサポートしている可能性があります。オフセットなどを変更せずに古いコードを新しいコードに置き換える作業を行う余地をデバッガーに提供します。


4
Edit andcontinue用にVSでデバッガーサポートを実装しました(CLRまたはコンパイラー部分は実装しませんでした)。Nopsは、メソッドの古いバージョンから新しいバージョンへの正しいマッピングを保証するためのストーリーの一部です(特に、実行処理コードからジャンプした場合)。ただし、MSILの置き換えは、機能全体に基づいて行われます。CLRマネージコードの場合、その部分を実行するためにmsilに「余地を残す」必要はありません。ここで言うことは、ネイティブの編集と続行に関して正しいです。
スティーブシュタイナー

4

やや非正統的な使用法は、バッファオーバーフローの悪用で使用されるNOP-Slidesです。


これを探していました:)
Suraj Jain

リンクを修正してください。機能していません。404が見つからないため、Webアーカイブリンクを送信したことを常に確認してください。
Suraj Jain 2017

現在シェルコードに取り組んでおり、NOPに関するより多くのコンテキストが必要でした。少し戻る必要がありましたが、ここにアーカイブがあります:web.archive.org/web/20110124015428/http
//www.phreedom.org

4

50年遅すぎますがねえ。

Nopは、アセンブリコードを手動で入力する場合に役立ちます。コードを削除する必要がある場合は、古いオペコードを削除できます。

同様に、オペコードを上書きして別の場所にジャンプすることで、新しいコードを挿入できます。そこに上書きされたオペコードを置き、新しいコードを挿入します。準備ができたら、ジャンプして戻ります。

時々あなたは利用可能なツールを使わなければなりませんでした。場合によっては、これは非常に基本的なマシンコードエディタでした。

今日のコンパイラでは、この手法はもはや意味がありません。


3

それらの古典的な使用法の1つは、デバッガーが常にソースコード行をIL命令に関連付けることができるようにすることです。


msilは実行時にJITコンパイルされるため、そのマッピングが失われる可能性があります(たとえば、ネイティブ命令には一意のMSIL命令がありません)。NOPは、言語コンパイラからJITコンパイラへの通信メカニズムとして使用され、このようなマッピングを保持します。
スティーブシュタイナー

3

ソフトウェアクラッキングシーンでは、アプリケーションのロックを解除する古典的な方法は、キー、登録、期間などをチェックする行にNOPでパッチを適用することです。これにより、何も行われず、登録されているかのようにアプリケーションを起動し続けることができます。 。


5
人々がソフトウェアを海賊版にするのを助けるために、操作禁止の指示が発明されなかったと私はかなり確信しています:-)
Simon Howard

3

また、プレースホルダーとしての機能を難読化するようにコード内のNOPを確認しました(非常に古いコピー防止)。


3

ddaaが言ったように、nopsを使用するとスタックの変動を考慮できるため、リターンアドレスを上書きすると、nopスレッド(連続した多数のnop)にジャンプしてから、実行可能コードにジャンプするのではなく、正しくヒットします。開始ではない命令のバイト。


1

これにより、リンカは長い命令(通常は長いジャンプ)を短い命令(短いジャンプ)に置き換えることができます。NOPは余分なスペースを取ります。他のジャンプが機能しなくなるため、コードを移動できませんでした。これはリンク時に発生するため、コンパイラはロングジャンプとショートジャンプのどちらが適切かを判断できません。

少なくとも、それは彼らの伝統的な使用法の1つです。


1

これはあなたの特定の質問に対する答えではありませんが、昔は、NOPを使用して分岐遅延スロットを埋めることができましたが、それ以外の場合に役立つ命令で埋めることができなかった場合です。


1

.NETコンパイラはMSIL出力を調整しますか?ILへのアクセスを高速化するのに役立つかもしれないと思います...また、移植可能に設計されており、他のハードウェアプラットフォームでは調整されたアクセスが必要であると理解しています。


1

私が最初に学んだアセンブリはSPARCだったので、分岐遅延スロットに精通しています。別の命令、通常は分岐命令の上に配置する命令、またはループでカウンターをインクリメントする命令で埋めることができない場合は、 NOP。

私はクラッキングに精通していませんが、NOPを使用してスタックを上書きするのが一般的であるため、悪意のある関数がどこから始まるかを正確に計算する必要はないと思います。


1

NOPを使用して、ISRに入った後に累積されたレイテンシーを自動的に調整しました。タイミングを釘付けにするのに非常に便利です。


-1

nopメモリ破損エクスプロイトペイロードに役立ちます。もちろん、nopと似ていますxchg eax, eax


別の回答では、コードインジェクションエクスプロイトのNOPスライドについてすでに言及しています。それで、この同じ質問に対して別の答えをしました。この回答は、それらがどのように役立つかについては言及ておらず、あなたがまだ知らないかどうかは明らかではありません。
ピーターコーデス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.