Upgrade to Pro — share decks privately, control downloads, hide ads and more …

The NOPs You Don't Know

herumi
October 03, 2024
400

The NOPs You Don't Know

herumi

October 03, 2024
Tweet

Transcript

  1. • 何もしない1byteの命令 • xchg eax, eaxのalias(32bit時代) • AArch64 • NOP

    = hint #0 • 昔のArmはmov x0, x0だったことも • RISC-V • nop = addi x0, x0, 0 # x0はゼロレジスタ 90h(=0x90) 3 / 14
  2. • 何もしない2byte命令(32bit) • lea eax, [eax] • 1byte nopを2個実行するより効率がよい(よかった) •

    89 c0h • 別の何もしない2byte命令 • mov eax, eax • 8d 04 05 00 00 00 00h • 何もしない7byte命令 • lea eax, [eax * 1 + 0x00000000] • ... 8d 00h 4 / 14
  3. • これらのnopたちは何もしないわけではなくなった • 32bitレジスタ操作命令は64bitレジスタの上位を クリアする • パーシャルレジスタストールを避けるために 値を保持する戦略はとらなかった(推測) • xchg

    eax, eax / lea eax, [eax]などもnopでなくなった • mov eax, eaxはxxxxxxxxを0にする • movzx rax, eaxと同じ(というかこの命令は存在しない) 64bit環境では rax(64) eax(32) ax(16) xxxxxxxx 5 / 14
  4. • 90hは本当のnopになった • xchg eax, eaxではない • それではxchg eax, eaxはどうなった?

    • 87, c0h ; xchgの冗長表現 • xchg rax, raxは48 90h • 意味的には32bitのnopに対応するが内部的にはnopではない 64bitでのnop 6 / 14
  5. • いつからnopが拡張された(2000年頃?) • nop r/m • nopがレジスタやメモリアドレッシングを引数に取る • 32/64bit両対応で同一コード •

    AMD CPUでも公式にサポートされてるので安心 • 大昔はIntelとAMDで異なる長いnopを準備したことも multi byte nop 66 nop 66 90h nop dword ptr [eax] 0f 1f 00h nop dword ptr [eax + 00h] 0f 1f 40 00h nop dword ptr [eax + eax*1 + 00h] 0f 1f 44 00 00h 66 nop dword ptr [eax + eax*1 + 00h] 66 0f 1f 44 00 00h nop dword ptr [eax + 00000000h] 0f 1f 80 00 00 00 00h nop dword ptr [eax + eax*1 + 00000000h] 0f 1f 84 00 00 00 00 00h 66 nop dword ptr [eax + eax*1 + 00000000h] 66 0f 1f 84 00 00 00 00 00h 7 / 14
  6. • multi byte nopはeax以外の引数も取れる • nop ecx, eax ; 0f

    1f c1h • nop esp, [rax + rax] ; 0f 1a 04 00h • Intelはこの余ってるnopを拡張した multi byte nopの拡張 8 / 14
  7. • バッファオーバーフローをOS/libレベルでチェック • 4個の128bit boundレジスタbnd0, bnd1, bnd2, bnd3 • 上位64bitは境界の上限,

    下位64bitは境界の下限の値が入る • bndmk bnd, mem • memの値をbndレジスタに設定する • bndcl bnd, r/m • r/mの値がbndの下限より大きいかを確認 • bndcu bnd, r/m • r/mの値がbndの上限より小さいかを確認 • 範囲外ならbounds violationが発生 MPX(Memory Protection Extensions) 9 / 14
  8. • MPX非対応CPU/OSで影響が出ないように拡張 • 対応していないとこう見える MPXエンコーディング bndmk bnd0, [rax] ; f3

    0f 1b 00h bndcl bnd0, rax ; f3 0f 1a c0h bndcu bnd0, rax ; f2 0f 1a c0h bndldx bnd0, [rax+rax] ; 0f 1a 04 00h bndstx [rax+rax], bnd0 ; 0f 1b 04 00h nop eax, [rax] nop eax, eax nop eax, eax nop esp, [rax+rax] nop esp, [rax+rax] [rax*2]と等しくない 省略可能な3引数オペランド の一部としてSIBを利用 10 / 14
  9. • コンパイラが対応してくれなかった • 2024年Intel APX登場! • 3op/32レジスタ拡張 add, r20, r21,

    r23 (まるでRISC-V) • xsave/xsaveopt • ユーザモードでレジスタの退避・復元をするための命令 • MPXのための領域128バイトがあった • 4個の128bitレジスタ+状態 • これはもう使われることはない • それを拡張レジスタの保存に使うことになった 2019年MPXはremoved 11 / 14
  10. • 単純な関数で見てみる • endbr64って何? 関数の先頭のnop id: endbr64 mov eax, edi

    # eax = x; ret 12 / 14 int id(int x) { return x; } gcc –O2 –S t.c
  11. • Control-flow Enforcement Technology • ROP攻撃を防ぐ • 実行ファイルの中にある細切れの命令列を実行する攻撃手法 • バイト列の途中にジャンプしてくる

    • CETが有効だと • 正しいコードの先頭にはendbr64を入れてマークしておく • マークされたバイト列にしかジャンプできない • おかしなところにジャンプできない • CETが無効の古い環境では長いnopと解釈される • バイトコード F3 0F 1E FB = rep nop ebx • F3 : 大昔は繰り返し(rep)を意味することが多かったが 今はなんでもあり CET 13 / 14