x86アセンブリでのNOP命令とalignステートメントの目的


15

アセンブリクラスを最後に受講してから1年ほど経ちました。そのクラスでは、IrvineライブラリでMASMを使用して、プログラミングを簡単にしました。

ほとんどの指示を終えた後、彼はNOP指示は基本的に何もせず、それを使うことを心配しないと言った。とにかく、それは中間期であり、適切に実行されないサンプルコードがあるため、NOP命令を追加するよう指示され、正常に機能しました。私は授業の後で、なぜそれが実際に何をしたのかと尋ねました。

誰か知ってる?


NOPは何も行いませんが、サイクルを消費します。推測しかできないコードがなければ、あなたの質問に答えられるとは思いません。まあ、私の推測は次のようになりNOPスライド ...
ヤニス

11
NOPは実際に何かをします。命令ポインタをインクリメントします。
EricSchaefer

回答:


37

多くの場合、NOP命令アドレスの調整に時間が使用されます。これは通常、バッファオーバーフローまたはフォーマット文字列の脆弱性を悪用するためにシェルコードを記述するときに発生します

100バイト前方への相対的なジャンプがあり、コードにいくつかの変更を加えたとします。修正がジャンプターゲットのアドレスを台無しにする可能性があるため、前述の相対的なジャンプも変更する必要があります。ここでNOP、ターゲットアドレスを前方にプッシュするためにsを追加できます。NOPターゲットアドレスとジャンプ命令の間に複数のsがある場合、NOPsを削除してターゲットアドレスを逆方向にプルできます。

ラベルをサポートするアセンブラを使用している場合、これは問題になりません。単純に行うことができますJXX someLabel(JXXは条件付きジャンプです)。アセンブラーはsomeLabelそのラベルのアドレスに置き換えます。ただし、アセンブルされたマシンコード(実際のオペコード)を手動で変更する場合(シェルコードの作成時に発生することがあります)、ジャンプ命令も手動で変更する必要があります。変更するか、NOPs を使用してターゲットコードアドレスを移動します。

NOP命令の別のユースケースは、NOPスレッドと呼ばれるものです。本質的には、副作用を引き起こさない命令の十分に大きい配列を作成することですNOPまたは、レジスタをインクリメントしてからデクリメントしますが、命令ポインタを増やします。これは、たとえば、アドレスが不明な特定のコードにジャンプしたい場合に便利です。トリックは、上記のNOPスレッドをターゲットコードの前に配置し、そのスレッドのどこかにジャンプすることです。何が起こるかというと、副作用のない配列から実行がうまくいけば、望みのコードに到達するまで命令ごとに命令を進めます。この手法は、前述のバッファオーバーフローの悪用や、特にASLRなどのセキュリティ対策に使用されます。

NOP命令のさらに別の特定の用途は、あるプログラムのコードを変更するときです。たとえば、条件付きジャンプの一部をNOPsに置き換えて、条件を回避できます。これは、ソフトウェアのコピー保護を「クラック」するときによく使用される方法です。最も簡単なのif(genuineCopy) ...は、コード行のアセンブリコード構成を削除し、命令をNOPsおよび....に置き換えることです。チェックは行われず、非正規のコピーが機能します!

本質的に、シェルコードとクラッキングの両方の例は同じことを行うことに注意してください。相対アドレス指定に依存する操作の相対アドレスを更新せずに、既存のコードを変更します。


2
これは素晴らしい答えでした。これを説明するために時間を割いてくれてありがとう!ついにわかりました!
-alvonellos

特定のリアルタイムシステム(PLCが思い浮かぶ)を使用すると、実行中に既存のプログラムに新しいロジックを「パッチ」することができます。これらのシステムは、ロジックの小さな部分の前にNOPを残すため、挿入する新しいロジックへのジャンプでNOPを上書きできます。新しいロジックの最後で、置換する元のロジックの最後にジャンプします。また、新しいロジックにはNOPが前面にあるため、新しいロジックも置き換えることができます。
スコットホイットロック

10

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システムでは、それが答えかもしれません。


4
質問にはx86というタグが付けられており、x86には遅延スロットがないことに注意してください。それは壊滅的な変化だからです。
MSalters

6

NOPを使用する理由の少なくとも1つはアライメントです。x86プロセッサは、非常に大きなブロックでメインメモリからデータを読み取ります。読み取りブロックの開始位置は常に調整されているため、読み取りが多いコードブロックがある場合は、このブロックを調整する必要があります。これにより、速度が少し向上します。


ブロックを整列する必要があるというわけではありません。前のブロックの最後の数バイトをフェッチする必要はありません。だから、にジャンプし0x1002ても問題ありません0x099D。ターゲットアドレスを含む16Bのアライメントされたブロックにはまだ14バイトの命令がありますが、にジャンプしても問題ありません。
ピーターコーデス

3

NOP(x86だけでなく一般的なアセンブリ)の1つの目的は、時間遅延を導入することです。たとえば、1秒の遅延でいくつかのLEDに出力しなければならないマイクロコントローラーをプログラムしたいとします。この遅延は、NOP(およびブランチ)で実装できます。もちろん、ADDなどを使用することもできますが、それによりコードが読みにくくなります。または、すべてのレジスタが必要な場合があります。


1
通常、1秒などの長い時間フレームでは、タイマーが使用されます。NOPSは、ナノ秒およびマイクロ秒のクロックの大きさの範囲内でエポックに使用されます。
mattnz

これは、最新のx86ではなく、マイクロコントローラーでのみ意味があります。ほとんどのx86コードは、最新のスーパースカラーアウトオブオーダーCPUのパイプライン幅を飽和させないため、ほとんどのコードのすべての命令間にNOPを追加しても、わずかな影響しかありません(「平均」コードの数は何の減速が、ほぼ2倍の減速を示すいくつかのタイトなループを示していないいくつかのコードを、命令の数を2倍にする〜20%。)とにかく、無愛想な古いx86のコード5は、伝統的に使用された遅延ループの指示を、ないのNOP。loop
ピーターコーデス

3

一般的に80x86では、プログラムの正確性のためにNOP命令は必要ありませんが、戦略的に配置されたNOPによりコードがより速く実行されるマシンもある場合があります。たとえば、8086では、コードは2バイトのチャンクでフェッチされ、プロセッサには3つのそのようなチャンクを保持できる内部「プリフェッチ」バッファがありました。フェッチできるよりも高速に実行される命令もあれば、実行に時間がかかる命令もあります。遅い命令の間、プロセッサはプリフェッチバッファをいっぱいにしようとするため、次のいくつかの命令が高速だった場合は、それらをすばやく実行できます。スロー命令に続く命令が偶数ワード境界で開始する場合、次の6バイトに相当する命令がプリフェッチされます。奇数のバイト境界で始まる場合、5バイトのみがプリフェッチされます。

このようなメモリアライメントの問題はプログラムの速度に影響を与える可能性がありますが、通常は正確性には影響しません。一方、古いプロセッサでは、NOPが正確性に影響を与える可能性のあるプリフェッチ関連の問題がいくつかあります。命令が既にプリフェッチされたコードバイトを変更すると、8086(および80286と80386が)はメモリ内の内容と一致しなくてもプリフェッチされた命令を実行します。メモリを変更する命令と変更されるコードバイトの間にNOPまたは2を追加すると、コードバイトが書き込まれるまでフェッチされない場合があります。ところで、多くのコピー防止スキームがこの種の動作を悪用したことに注意してください。ただし、この動作は保証されないことにも注意してください。プロセッサの種類によって、プリフェッチの処理方法が異なる場合がありますが、読み取り元のメモリが変更された場合、プリフェッチされたバイトを無効にするものもあり、割り込みは一般的にプリフェッチバッファを無効にします。割り込みが戻ると、コードが再取得されます。


3

他の回答で説明されていないx86固有のケースがあります:割り込み処理。いくつかのスタイルでは、メインコードが割り込みハンドラーと共有されているデータを処理するため、割り込みが無効になっているコードセクションがありますが、そのようなセクション間で割り込みを許可するのが妥当です。素朴に書くなら


    STI
    CLI

Intelを引用して、保留中の割り込みは処理されません。

IFフラグが設定された後、次の命令が実行された後、プロセッサは外部のマスク可能な割り込みへの応答を開始します。

したがって、これは少なくとも次のように書き換えられます。


    STI
    NOP
    CLI

2番目のバリアントでは、保留中のすべての割り込みがNOPとCLIの間でのみ処理されます。(もちろん、STI命令を2倍にするなど、多くの代替バリアントが存在する可能性があります。しかし、少なくとも私にとっては、明示的なNOPはより明白です。)


-2

NOPは操作なしを意味します

一般に、マシンコードの挿入または削除、または特定のコードの実行の遅延に使用されます。

また、クラッカーおよびデバッガーがブレークポイントを設定するために使用します。

おそらく、XCHG BXのようなことをすると、BXも同じ結果になります。

まだ処理中の操作がほとんどないため、エラーが発生したかのように聞こえます。

あなたがVBに精通しているなら、私はあなたに例を与えることができます:

vbでログインシステムを作成し、3つの異なるタブでfacebook、youtube、twitterの3つのページを一緒にロードする場合。

すべてに1つのログインボタンを使用します。インターネット接続が遅い場合、エラーが発生する可能性があります。つまり、ページの1つがまだロードされていません。そこで、これを克服するためにApplication.DoEventsを導入しました。アセンブリNOPで同じ方法を使用できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.