私が最後に総会を受講してから1年ほどになります。そのクラスでは、IrvineライブラリでMASMを使用して、プログラミングを容易にしました。
ほとんどの手順を実行した後、彼はNOP命令は本質的に何もせず、それを使用することについて心配する必要がないと述べました。とにかく、それは中期くらいでした、そして、彼は適切に動かないいくつかのサンプルコードを持っているので、彼は私たちにNOP命令を追加するように言って、それはうまくいきました。私はクラスの後で、なぜ、それが実際に何をしたのかと尋ねると、彼は知らないと言った。
誰か知ってる?
多くの場合、NOP
は命令アドレスを整列させるために使用されます。これは通常、たとえば、シェルコードを書き込んで バッファオーバーフロー または フォーマット文字列の脆弱性 。
たとえば、100バイト先に相対的にジャンプし、コードに変更を加えたとします。可能性としては、変更がジャンプターゲットのアドレスをめちゃくちゃにするため、前述の相対ジャンプも変更する必要があります。ここでは、NOP
sを追加して、ターゲットアドレスを前方にプッシュできます。ターゲットアドレスとジャンプ命令の間に複数のNOP
sがある場合は、NOP
sを削除してターゲットアドレスを後方にプルできます。
ラベルをサポートするアセンブラを使用している場合、これは問題にはなりません。単純に_JXX someLabel
_(JXXは条件付きジャンプ)を実行でき、アセンブラはsomeLabel
をそのラベルのアドレスに置き換えます。ただし、アセンブルされたマシンコード(実際のオペコード)を手動で変更するだけの場合(シェルコードの書き込みで発生する場合があるため)、ジャンプ命令を手動で変更する必要もあります。変更するか、NOP
sを使用してターゲットコードアドレスを移動します。
NOP
命令の別のユースケースは、 NOPスレッド と呼ばれるものです。本質的には、副作用を引き起こさない(NOP
やレジスターのインクリメントとデクリメントなど)命令のポインターを増加させる、十分に大きな命令の配列を作成するという考え方です。これは、アドレスが不明な特定のコードにジャンプする場合などに便利です。トリックは、ターゲットNOPスレッドをターゲットコードの前に配置し、そのスレッドにどこかにジャンプすることです。何が起こるかと言うと、実行は副作用のない配列から続行され、目的のコードに到達するまで、命令ごとの命令を順方向に走査します。この手法は、前述のバッファオーバーフローエクスプロイトで、特に [〜#〜] aslr [〜#〜] などのセキュリティ対策に対応するために一般的に使用されています。
NOP
命令のさらに別の特定の用途は、あるプログラムのコードを変更する場合です。たとえば、条件付きジャンプの一部をNOP
sに置き換えて、条件を回避することができます。これは、ソフトウェアの "cracking"コピー防止機能でよく使用される方法です。簡単に言えば、コードのif(genuineCopy) ...
行のアセンブリコード構成を削除し、命令をNOP
sに置き換えるだけです。チェックは行われず、非正規のコピーが機能します!
本質的に、シェルコードとクラッキングの両方の例は同じことを行うことに注意してください。相対アドレッシングに依存する操作の相対アドレスを更新せずに既存のコードを変更します。
A 遅延スロット でnopを使用すると、他の命令を再配置してそこに配置することができません。
lw v0,4(v1)
jr v0
MIPSでは、これはバグです。これは、jrがレジスタv0を読み取っていたとき、レジスタv0には前の命令からの値がまだロードされていないためです。
これを修正する方法は次のとおりです。
lw v0,4(v1)
nop
jr v0
nop
これは、ワードのロードおよびレジスタのジャンプ命令の後にディーリースロットをnopで埋めるため、ワードのロード命令は、レジスタのジャンプコマンドが実行される前に完了します。
さらに読む-少しビットSPARC 遅延スロットの充填 。そのドキュメントから:
遅延スロットには何を入れることができますか?
- 分岐するかどうかにかかわらず実行する必要があるいくつかの便利な命令。
- 分岐するとき(または分岐しないとき)にのみ機能するが、それ以外の場合に実行されても害を及ぼさない命令。
- 他のすべてが失敗すると、NOP命令
遅延スロットに何を入れてはいけませんか?
- 分岐の決定が依存するCCを設定するもの。分岐命令は、すぐに分岐するかどうかを決定しますが、実際には、遅延命令の後まで分岐を行いません。 (分岐のみが遅延され、決定は遅延されません。)
- 別の分岐命令。 (これを定義しないとどうなりますか?結果は予測できません!)
- 「セット」命令。これは実際には1つの命令ではなく2つの命令であり、その半分だけが遅延スロットにあります。 (アセンブラーはこれについて警告します。)
遅延スロットに入れるものの3番目のオプションに注意してください。あなたが見たバグはおそらく、誰かが遅延スロットに入れてはならないものの1つを満たしていることでした。その場所にnopを置くと、バグが修正されます。
注:質問をもう一度読んだ後、これはx86の場合で、遅延スロットはありません(分岐はパイプラインをストールさせるだけです)。そのため、それはバグの原因/解決策ではありません。 RISCシステムでは、それが答えである可能性があります。
nOPを使用する少なくとも1つの理由はアライメントです。 x86プロセッサは非常に大きなブロックでメインメモリからデータを読み取り、読み取るブロックの先頭は常にアラインされているため、コードブロックがある場合は、それが多く読み取られる場合、このブロックをアラインする必要があります。これは少しスピードアップになります。
他の回答ではまだ説明されていないx86固有のケースがあります:割り込み処理。一部のスタイルでは、割り込みが無効になっているときにコードセクションが存在する可能性があります。これは、メインコードが割り込みハンドラーと共有される一部のデータで機能するためです。ただし、そのようなセクション間の割り込みを許可するのが妥当です。単純に書けば
STI
CLI
intelを引用しているため、これは保留中の割り込みを処理しません。
IFフラグが設定された後、次の命令が実行された後、プロセッサは外部のマスカブル割り込みへの応答を開始します。
したがって、これは少なくとも次のように書き直されます。
STI
NOP
CLI
2番目のバリアントでは、すべての保留中の割り込みは、NOPとCLIの間でのみ処理されます。 (もちろん、STI命令を2倍にするなど、多くの代替バリアントが存在する可能性があります。しかし、明示的なNOPは、少なくとも私にとってはより明白です。)
NOP(x86だけではなく一般的なアセンブリ)の1つの目的は、時間遅延を導入することです。たとえば、1秒の遅延で一部のLEDに出力する必要があるマイクロコントローラーをプログラムしたいとします。この遅延は、NOP(およびブランチ)で実装できます。もちろん、ADDなどを使用することもできますが、そうするとコードが読みにくくなります。または多分あなたはすべてのレジスタが必要です。
一般的に80x86では、プログラムを正確にするためにNOP命令は必要ありませんが、一部のマシンでは戦略的に配置されたNOPによりコードがより速く実行される場合があります。たとえば、8086では、コードは2バイトのチャンクでフェッチされ、プロセッサにはそのような3つのチャンクを保持できる内部「プリフェッチ」バッファがありました。一部の命令はフェッチよりも速く実行されますが、他の命令は実行に時間がかかります。遅い命令の間、プロセッサはプリフェッチバッファをいっぱいにしようとするため、次のいくつかの命令が高速である場合、それらは迅速に実行できます。スロー命令に続く命令が偶数ワード境界で開始する場合、次の6バイトに相当する命令がプリフェッチされます。奇数バイト境界で開始する場合、プリフェッチされるのは5バイトのみです。私が思い出すと、NOPは3サイクルかかり、メモリフェッチは4サイクルかかったので、余分なバイトをプリフェッチするとメモリサイクルを節約できる場合、「NOP」を追加すると、遅いワードが偶数ワード境界で開始した後に命令が発生することがあります。サイクルを保存します。
このようなメモリ配置の問題はプログラムの速度に影響を与える可能性がありますが、一般的には正確性には影響しません。一方、NOPが正確さに影響を与える可能性がある古いプロセッサには、プリフェッチ関連の問題がいくつかあります。命令がすでにプリフェッチされているコードバイトを変更する場合、8086(および80286と80386)はプリフェッチされた命令を実行しますが、メモリ内のものと一致しなくなります。メモリを変更する命令と変更されたコードバイトの間に1つまたは2つのNOPを追加すると、コードバイトが書き込まれるまでフェッチされない場合があります。ところで、多くのコピー防止スキームがこの種の振る舞いを悪用していることに注意してください。ただし、この動作は保証されないことにも注意してください。プロセッサのバリエーションによってプリフェッチの処理が異なる場合があり、プリフェッチされたバイトが読み取られたメモリが変更された場合、プリフェッチされたバイトが無効になる場合があります。一般に、割り込みによってプリフェッチバッファが無効になります。割り込みが戻ると、コードが再フェッチされます。