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

RG-Arch 輪講資料: Binary Hacks Rebooted 数値演算など

RG-Arch 輪講資料: Binary Hacks Rebooted 数値演算など

Avatar for kota-yata

kota-yata

October 20, 2025
Tweet

More Decks by kota-yata

Other Decks in Programming

Transcript

  1. ⽬次 • 1章: イントロダクション ◦ fileコマンドを使わずにファイルの種類を調べる ◦ libcのwrite()を使った標準出⼒ • 7章:

    数値表現とデータ処理Hack ◦ 符号付き整数 ◦ エンディアン ◦ 固定⼩数点数 ◦ 可変⻑表現(Zig-zag, UTF-8) ◦ 浮動⼩数点数 ◦ NaN 2
  2. 1章-fileコマンドを使わずにファイルの種類を調べる 3 • fileコマンドは特定のファイルの形式,属性を調べるUnixコマンド ◦ 拡張⼦がなくても調べられる • 多くのファイルにはマジックナンバーがあり,最初の数バイトを⾒ると種類が 分かる ◦

    e.g. jpgはffd8ffe0,gzipは1f8b ◦ https://en.wikipedia.org/wiki/List_of_file_signatures • コマンド⾃体のhexdumpをするとELFのマジックナンバーが⾒えたりする ◦ Unixにおいて「全てはファイル」 • 古い.tarなどはマジックナンバーを持たず,独⾃の判定ロジックがある
  3. 1章-libcのwrite()を使った標準出⼒ 4 • Unix系のOS(Mac OSやLinux OS)においてはファイルディスクリプタの1番 は標準出⼒にアサインされている ◦ ちなみに0番は標準⼊⼒,2番は標準エラー出⼒ ◦

    ターミナルが標準出⼒”ファイル”を開いているというイメージ • print(“Hello world!”)は実質標準出⼒ファイルに書き込みしているのと⼀緒 • つまりwrite()でもprintと同じことができる!
  4. 補数表現を使うことのメリット 例:-7 -> 0b10000111 とすれば下位7ビットは符号なし整数と同じ.. • 減算を加算で表現できる ◦ 例:7-8を7+(-8)とする ◦

    0b00000111 + 0b11111000 = 0b11111111 = -1 ◦ 負の値同⼠の加算もオーバーフローを無視すれば同様に演算が可能 • 0が⼀意に決まる ◦ ⼀番上の例だと+0と-0という⼆つの0が誕⽣する ◦ 0が⼀意に決まる(0b0)補数表現の⽅が楽 8 「MSBを符号ビットとすればそのあとは値をそのまま格納できて直感的では?」
  5. 符号拡張 9 • CPUアーキテクチャによっては8ビットや16ビットの変数に対応する幅の命令 がない場合がある ◦ x86-64では切り離せなかった後⽅互換性により8, 16, 32, 64ビット全ての幅に対す

    る整数演算命令がある ◦ Arm64など⽐較的新しいアーキテクチャでは32ビットと64ビット幅の整数演算命令 しかない • 演算の前後でレジスタのビット幅に合致するように値を拡張する必要がある ◦ 上位に挿⼊する各桁を符号ビットと同じ値にすれば良い ◦ e.g. 11111001 -> 1111111111111001
  6. エンディアン 11 • ネットワークパケットのヘッダー情報はビッグエンディアンと定められている ◦ これはネットワークバイトオーダーと呼ばれる ◦ 各プロトコルで明確に定められているというよりはRFC1700などで慣習として記さ れている •

    ⼀⽅エンド端末では⼀般にリトルエンディアンが⽤いられる ◦ 幅が異なる変数にキャストした時とかに下位ビットが保持されるメリットがある ◦ ネットワークパケットを作る際はhton関数で変換するのが⼀般的 ◦ x86_64にはbswapやmovbe(拡張命令セット)などの変換命令がある
  7. 可変⻑表現-Zig-zagエンコーディング 15 • 2の補数を⽤いる符号付き整数とは対照的に,LSBを符号ビットとする ◦ 0b00000000: 0 ◦ 0b00000001: -1

    ◦ 0b00000010: 1 ◦ 0b00000011: -2 ◦ 0b00000100: 2 ◦ … ◦ 0b01111111: -127 ◦ 0b11111110: 127 ◦ 0b11111111: -128 例:-1を符号付き整数で表現したい時 • 2の補数表現の場合LEB128で10バイト • Zig-zagを使えばLEB128で1バイト! ⼩さな値は必ず少ないバイト数で表現できる
  8. おまけ:UTF-16 17 • Unicodeコードポイント U+0000〜U+FFFFはそのまま2バイトで表現 ◦ ⽇本語の常⽤漢字も2バイト! • U+10000〜U+10FFFFは「サロゲート」に分割される ◦

    UnicodeにおいてU+D800〜U+DBFFは上位サロゲート,U+DC00〜U+DFFFは下位 サロゲートという予約領域になっている ◦ コードポイント-0x10000をして,結果の上位10ビットを上位,下位10ビットを下 位サロゲートに格納
  9. UTF-16エンコードの例 18 • 0x1F97A - 0x10000 = 0xF97A = 0b00001111100101111010(20ビット)

    • 上位10桁と0xD800(上位サロゲートのベース値)と⾜す -> 0xD83E • 残りの桁と0xDC00(下位サロゲートのベース値)と⾜す -> 0xDD7A 🥺はD8 3E DD 7Aの4バイトにエンコードされる 🥺 (U+1F97A)
  10. 浮動⼩数点の正規化 21 • IEEE754では,浮動⼩数点の仮数部は常に”正規化”される ◦ 仮数部のMSBが0でない数になる(e.g. 0.034 -> 3.4, 0.254

    -> 2.54) ◦ つまり2進数で表現する時,整数部分は必ず1になるように指数が調整される ▪ e.g. 0.00101 -> 1.01 * 2^-3 ▪ 必ず1なので,実際には仮数部は⼩数点以下から格納される(ケチ表現) • 指数部は”バイアス”をかけて符号なし整数にする ◦ 例えば指数部が8ビットだった場合,実際の値+127をする ◦ 本来-126~127の値が,1~254になる(0は0.0, 255はNaNとinfiniteの表現に使う)
  11. 浮動⼩数点の加算 23 • 加算対象の⼆つの値の内指数が⼤きい⽅に合わせる ◦ 1.111 * 2^2 + 1.011

    * 2^3の場合指数3に合わせる ◦ 1.111 * 2^2 -> 0.1111 * 2^3 ◦ この段階で⽚⽅の精度が落ちる • 指数を合わせたら仮数部を⾜す ◦ 0.1111 + 1.011 = 10.0101 • 正規化する ◦ 1.00101 * 2^4
  12. NaNは2種類ある Signaling NaN 26 • 演算の⼊⼒に含まれているとinvalid operation例外を発⽣させる • 未初期化変数を誤って演算に使った際などの検出に使う Quiet

    NaN • 主に数値演算の結果として⽣成されるNaN • 演算時のinvalid operationの結果はデフォルトでquite NaN 表現としては,仮数部のMSBが1ならquiet NaN,0ならsignaling NaN…