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

The NOPs You Don't Know

Avatar for herumi herumi
October 03, 2024
510

The NOPs You Don't Know

Avatar for herumi

herumi

October 03, 2024
Tweet

More Decks by herumi

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