第53回プログラミング・シンポジウムでx86 JITコンパイラの脆弱性について発表します

今回思い切って情報処理学会の第53回プログラミング・シンポジウムにて「x86 JITコンパイラ上で任意コードを実行する方法」という題目の発表をすることになりました。

発表の背景

2011年2011年3月31日に設立したサイボウズ・ラボユース新屋さん鈴木さんの作っているプログラムの成果が結構形になってきたため、そのお披露目と学生引率も兼ねて、今回自分もサーベイ論文を書いて応募してみました。特に新しい未知の脆弱性というわけでもなく、JIT-Sprayという現在のドキュメントフォーマット攻撃でよく使われている既知の手法をDEPとASLRの攻略という観点から8ページで日本語で解説したものです。英語の解説記事はBlackHatのような国際セキュリティカンファレンスの発表資料などでいっぱいあるのですが、日本語でJIT-Sprayに関してまとまった記事を見かけなかったので自分で書いてみることにしました。今回の発表に際し、応募時に情報処理学会に著作権譲渡しているので、最初の1ページ目のアブストラクトのプレビューだけ少し紹介します。

x86 JITコンパイラ上で任意コードを実行する方法

おそらく大学を卒業して以来、10年ぶりにLaTexを使って論文を書いたと思います。図が思うように配置されず、\vspace*{3mm}とか少し使ってしまいました。すみません。

第53回プログラミング・シンポジウム(情報処理学会)

日程
2012年1月6日(金) 11:30 受付開始
2012年1月6日(金) 13:30 開会
2012年1月8日(日) 12:10 閉会

会場
ニューウェルシティ湯河原 (〒413-0001 静岡県熱海市泉107)

参加申込締切:11月25日(金) 12月6日(火) 延長しました

参加申込締切が12月6日(火)に 延長されていますので、発表プログラムに興味のある方はぜひ参加登録してみてください。宿泊費込みの参加費用がかかりますが、会場は熱海のホテルです。
プログラミング・シンポジウムは今回生まれて初めての参加となりますが、どうぞよろしくお願いいたします。

カテゴリー: x86

[Debug Hacks] #66.手元のx86マシンが64bitモード対応かどうかを調べる

本日オライリージャパン様より「Debug Hacks――デバッグを極めるテクニック&ツール」の献本をいただきました。著者の皆様、出版社の皆様ありがとうございます。

とりあえず、ざっくりと気になる章だけをかいつまんで読んでみたのですが、最後の章「#66.手元のx86マシンが64bitモード対応かどうかを調べる」では、/proc/cpuinfo で lm の文字列を探す方法と、以下のような CPUID 命令を発行して今自分が使っているマシンのCPUが64bitに対応しているかどうかを調べるハックが紹介されていました。

#include <stdio.h>

void cpuid(int op, unsigned int *eax, unsigned int *ebx, unsigned int *ecx, unsigned int *edx) {
  __asm__("cpuid" : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx) : "0" (op));
}


int main() {
  unsigned int eax, ebx, ecx, edx;
  cpuid(0x80000000, &eax, &ebx, &ecx, &edx);
  if (eax < 0x80000001) return 0;
  cpuid(0x80000001, &eax, &ebx, &ecx, &edx);
  if (!((1<<29) & edx)) return 0;
  printf("64bit Long mode is supported.\n");
  return 1;
}

ただし、このハックの問題点は、CPUが64bitをサポートしているかどうかだけをチェックしていて、実際に64bitロングモードか32bitのx86互換モードで動いているかどうかを検出できないことにあります。

そこで、現在のCPUが64bitロングモードで動作しているかどうかをチェックする簡単なPerlスクリプトを書いてみました。

#!/usr/bin/perl

use DynaLoader;

sub x86_sub {
my ($func, $x86) = @_;
if ($^O eq "linux") {
require 'syscall.ph';
syscall(&SYS_mprotect,(unpack"L",pack"P",$x86)&~4095,4096*2,7);
}
DynaLoader::dl_install_xsub(caller(0)."::$func",unpack"L",pack"P",$x86);
}

x86_sub check64bit => do {
our $LM64bit = "?";

"\xb8\x31\x00\x00\x00".    # mov eax, 0x31

"\x48".                    # dec eax // 64bit REX PREFIX
"\xa2".pack("P",$LM64bit). # mov [$LM64bit], al
"\xc3";                    # ret

};

&check64bit();

warn $LM64bit; # "0" => 32bit, "1" => 64bit

これは、DynaLoader::dl_install_xsubでx86の機械語を直接実行させるサブルーチンを定義して、64bitロングモード特有のREX PREFIXを解釈するかどうかで判別している方法になります。32bitのx86互換モードでは dec eax が実行され $LM64bit=’0′ となりますが、64bitロングモードではこのような1byte decは解釈されず、直後のmov命令のREX PREFIXを指定することになるので、$LM64bit=’1′ となります。

したがって、x86_64対応の「Intel(R) Xeon(R) CPU           E5430  @ 2.66GHz」が搭載されているマシンでもCentOS 5.2(i386)のような64bitに対応していないOSで動かしてしまっている場合は、32bitと判定されます。

本Hackはデバッグには関係ありませんが、普段スクリプト言語を書いておられる方にも興味を持っていただければ幸いです。

Pure Perl で Acme::x86::CPUID を作りました

Perlでアセンブリプログラミングしているとき、実行マシンのCPUを判別してプログラムの処理を分岐したいときがでてくると思います。

そういうときに困ってしまわないように Acme::x86::CPUID を作ってみました。

package Acme::x86::CPUID;

use DynaLoader;

sub ProcessorBrandString {
my $cpu = "\0" x 48;
my $x86 = ""
. "S\xbf" . pack("P", $cpu)
. "\xb8\x02\x00\x00\x80"
. ("P\x0f\xa2\x89\x07\x89_\x04\x89O\x08\x89W\x0c\x8d\x7f\x10X\x8d\@\x01" x 3)
. "[\xc3"
;
DynaLoader::dl_install_xsub("X",unpack"L",pack"P",$x86);&X;
$cpu =~ s/\0+//g;
$cpu =~ s/^ +//;
$cpu;
}

### print Acme::x86::CPUID::ProcessorBrandString, "\n";

1;

DynaLoaderはPerlコアに標準でバンドルされているとっても便利なモジュールです。

※ Acme::x86::CPUID は 100% Pure Perl で書かれているため、別途CコンパイラやXSモジュールなどをインストールする必要がありません。

■ 実行結果

以下、手元にある複数の環境で Acme::x86::CPUID::ProcessorBrandString の値を調べてみました。

※ Linux/Windows/FreeBSDのx86環境でそれぞれ動作確認しましたが、それ以外の環境ではシステムがクラッシュする可能性がありますので実行はお勧めしません。

1. WindowsXP(32bit) – ブログを書いているDELLデスクトップ

Intel(R) Pentium(R) 4 CPU 3.40GHz

2. CentOS5.2(i386) – wafful.org で使用しているVPSレンタルサーバ

Intel(R) Xeon(R) CPU           E5430  @ 2.66GHz

3. CentOS4.4(x86_64) – Linux/AMD64 の開発環境

AMD Opteron(tm) Processor 240 EE

4. FreeBSD(i386) – 奥さんから借りている jail 環境

Intel(R) Pentium(R) 4 CPU 3.20GHz

うまく CPU の情報を取ってこれているみたいですね!

■ まとめ

次回Shibuya.pmで発表の機会があれば、この辺の DynaLoader を活用した基礎的なテクニックを簡単にお話ししようと思います。

XS Niteよりも低レベルな内容ですので、Perlや正規表現に詳しくない人でも安心して聞けると思います。どうぞご安心ください。

連載企画 FizzBuzz ではじめる Code Golf (x86 32bit) 入門

Code Golf とは? Matzにっき(2006-10-05) より

ゴルフとは如何に少ないストロークでホールインするかを競う競技である。
コードゴルフとは、如何に少ないキーストローク(バイト数)で、プログラムを実装できるかを競う競技である。

先日FizzBuzz.com (MS-DOS 16bit版) を作ってみたら、32bit版のプログラムにも挑戦したくなりましたので、x86 32bitで命令長を減らすテクニックについて紹介したいと思います。

※まずはコード長の比較のみで実行クロック数は競わないことにします。

■ x86 32bit コード最適化

【問題】EBXレジスタに1を、EAXレジスタに4を代入したい

できるだけ短いバイト数でコードを実現するためには、いろいろなx86命令をフル活用することを考えます。

自分の思いついた解答をNASMの記法で書いてみます。

続きを読む

FizzBuzz x86 for バイナリアン

昨日の続き。今日は息抜きに FizzBuzz.com (MS-DOS 16bit版) を作ってみました。

0000000 b4 02 bb 31 30 30 ed e8 2c 00 e8 29 00 e8 39 00 
0000020 e8 23 00 e8 3e 00 e8 30 00 e8 1a 00 e8 17 00 e8
0000040 27 00 e8 2f 00 e8 0e 00 e8 1e 00 e8 08 00 e8 05
0000060 00 e8 13 00 eb d1 80 ff 30 74 04 88 fa cd 21 88
0000100 da cd 21 e8 28 00 c3 fe c5 b2 46 cd 21 b2 69 cd
0000120 21 e9 08 00 b2 42 cd 21 b2 75 cd 21 b2 7a cd 21
0000140 cd 21 84 ed 75 04 e8 05 00 c3 30 ed eb e6 b2 0d
0000160 cd 21 b2 0a cd 21 fe c3 80 fb 3a 75 09 b3 30 fe
0000200 c7 80 ff 3a 74 01 c3 b8 00 4c cd 21
0000214

これで140byte

続きを読む

Binary Hacks と 64bit popCount 問題

各所レポートが挙がっている通り、私の手元にも Binary Hacks の献本が届きました!

表紙
Binary Hacks サポートページ
書名 Binary Hacks
サブタイトル ハッカー秘伝のテクニック100選
著者 高林哲,
鵜飼文敏,
佐藤祐介,
浜地慎一郎,
首藤一幸
出版社 オライリー・ジャパン
定価 3,360円 (税込)
ページ数 412ページ
ISBN 4-87311-288-5
発売日 2006年11月11日
版型 A5

高林さん、オライリーさん、ありがとうございます。

ちなみにetoさん情報によると、明日11/10は「いいバイナリの日」らしいです。

  • 11 → いい
  • 10 → バイナリ

Binary Hacks の発売日は 11/11 で、ビットが全部立っている非常に縁起の良い日です。

縁起を担ぐためにも、いいバイナリの日に Binary Hacks を注文して、発売日に書店に行って本を見かけたら 11 冊買いましょう。

x86 パフォーマンスチューニング

さて、最後の HACK #100「文献案内」でマイクロプロセッサアーキテクチャマニュアルが紹介されていましたが、
x86のパフォーマンスチューニングつながりということで、
ちょうど今日ラボの社内掲示板で盛り上がった話題をこちらでも共有したいと思います。

* popCount 問題

64bitの数値の中で1になっているビット数を数える

popCount64bitA

unsigned long long popCount64bitA(unsigned long long x) 
{
    int n = 0;
    n += popTable[x & 0xff];
    n += popTable[(x >> 8) & 0xff];
    n += popTable[(x >> 16) & 0xff];
    n += popTable[(x >> 24) & 0xff];
    n += popTable[(x >> 32) & 0xff];
    n += popTable[(x >> 40) & 0xff];
    n += popTable[(x >> 48) & 0xff];
    n += popTable[(x >> 56) & 0xff];
    return n;
}

popCount64bitB

unsigned long long popCount64bitB(unsigned long long x) 
{
    x = ((x & 0xaaaaaaaaaaaaaaaaUL) >> 1)
      +  (x & 0x5555555555555555UL);
    x = ((x & 0xccccccccccccccccUL) >> 2)
      +  (x & 0x3333333333333333UL);
    x = ((x & 0xf0f0f0f0f0f0f0f0UL) >> 4)
      +  (x & 0x0f0f0f0f0f0f0f0fUL);
    x = ((x & 0xff00ff00ff00ff00UL) >> 8)
      +  (x & 0x00ff00ff00ff00ffUL);
    x = ((x & 0xffff0000ffff0000UL) >> 16)
      +  (x & 0x0000ffff0000ffffUL);
    x = ((x & 0xffffffff00000000UL) >> 32)
      +  (x & 0x00000000ffffffffUL);
    return x;
}

→ どちらが速いか?

続きを読む