HACKING

Hacking:美しき策謀―脆弱性攻撃の理論と実際 Hacking:美しき策謀―脆弱性攻撃の理論と実際
かなーり、面白い本だった。ノンストップで一気に読んでしまった。
バッファオーバーフロー攻撃などの大まかな原理はわかっていても、実際の攻撃プログラムの実装についてまで考えたことはなかった。この本には短いながらも実際に使える(?)危険なコードが載せられ、丁寧に説明されている。たとえばバッファーオーバフロー攻撃の1つはstrcpyが確保してある配列を超えて書き込むバグを利用するのだが、そのためには送り込むコードにヌル文字(=0x00)が含まれていてはいけない。ヌル文字があるとstrcpyのコピーが途中で止まってしまうからだ。てことで、バイト列に0を含まないようにするために、0をレジスタにロードする命令は 同一レジスタ同士の xor命令に置き換えたりなどのさまざまな工夫を施す必要がある。この本にはそういったテクニックが大量に、丁寧に説明されていて、読んでいて実に楽しい。

コーディング上のテクニックのほかに、暗号の解読方法や、ネットワーク盗聴の方法などにも言及してあって、防衛のためにもこれらに詳しくない人は読んでおくといいんじゃないかなと思った。

ASSEMBLY'05

ASSEMBLY'05の作品を見てみた。64k部門がいまいちだったけど、demo部門の1位と2位がよかった。

sts-04: instant zen
音楽がすごくよい。こういう悲しげで神秘的な曲って大好きだ。
3Dのロゴがモーションブラーがかかったようにぼやけなからモーフィングする処理はどうやってるんだろう?
sts_00001.jpgsts_00003.jpg
sts_00005.jpgsts_00007.jpg

Iconoclast
これまたプログレ調の音楽がすごい。ギターのクオリティ高すぎ。
PixelShader使いまくりなので、みれないひとは上記のリンクからビデオのほうをダウンロードしてそっちを見るべし。
去年もPlanet Riskという素晴らしい作品を発表したもののObsoleetに1位を奪われてしまったが、今回は文句なしに1位をゲット。幻想的で夢の中のような美しい画面が続くが、とくにシーン切り替えの演出が凝っていて流れるようにデモが進行していく。

23806facesの蜂のシーンは笑った。最近昔のデモのオマージュがはやってるなあ。好きだけど。
ico_00005.jpgico_00006.jpg
ico_00013.jpgico_00016.jpg
ico_00001.jpgico_00009.jpg

Cg の最適化

今まで Hamanaのシェーダーは アセンブラ(?)で書いていたのだが、nVIDIAの高級言語 Cg も触っとかないとな!ってことで、暇をみてCgを触っている。うーん、仕事以外のプログラミングってなんでこんなに楽しいのだろう。
んが、しかし、Cgのコンパイラが効率の悪いコードを吐き出していて困っている。

具体的には以下のCgのコードだ。
void FragmentProgram(float4 color : COLOR0,
  in float2 texCoord0 : TEXCOORD0,
  out float4 colorO : COLOR0,
  const uniform sampler2D Texture0,
  const uniform sampler1D Texture1)
{
  float4 texCol = tex2D(Texture0, texCoord0);
  colorO.r = tex1D(Texture1, texCol.r).r;
  colorO.g = tex1D(Texture1, texCol.g).r;
  colorO.b = tex1D(Texture1, texCol.b).r;
  colorO.a = texCol.a * color.a;
}
これはトーン補正を行うためのピクセルシェーダー のコードで Texture0がソースの画像をもつ2Dテクスチャ、Texture1 が各RGB成分から実際に出力するトーン値へのマッピングを保持する1Dテクスチャである。

これをCgコンパイラでps2.0用のコードにコンパイルすると、以下のようなアセンブリコードを出力する。
ps_2_0
// cgc version 1.4.0000, build date Jun  9 2005 12:09:02
// command line args: -profile ps_2_0
// source file: FragmentProgram.cg
//vendor NVIDIA Corporation
//version 1.0.02
//profile ps_2_0
//program FragmentProgram
//semantic FragmentProgram.Texture0
//semantic FragmentProgram.Texture1
//var float4 color : $vin.COLOR0 : COL0 : 0 : 1
//var float2 texCoord0 : $vin.TEXCOORD0 : TEX0 : 1 : 1
//var float4 colorO : $vout.COLOR0 : COL : 2 : 1
//var sampler2D Texture0 :  : texunit 0 : 3 : 1
//var sampler2D Texture1 :  : texunit 1 : 4 : 1
//const c[0] = 0.5
dcl_2d s0
dcl_2d s1
def c0, 0.500000, 0.000000, 0.000000, 0.000000
dcl t0.xy
dcl v0.xyzw
texld r0, t0, s0
mov r1.x, r0.y
mov r0.y, c0.x
mov r1.y, c0.x
mov r2.x, r0.z
mov r2.y, c0.x
mul r0.w, r0, v0
texld r3, r2, s1
mov r2.x, r3
texld r3, r0, s1
mov r0.x, r3
texld r3, r1, s1
mov r1.x, r3
mov r0.z, r2.x
mov r0.y, r1.x
mov oC0, r0

なぜか謎の定数 c0 が定義されていて、意味もなく汎用レジスタにmovしたりしている。

以前の、Cgを使わないでアセンブラで記述していたコードは以下のようになっていて、Cgの吐き出すコードよりもだいぶ短い。
ps_2_0
dcl v0
dcl t0
dcl_2d s0
dcl_2d s1

texld r0, t0, s0    // r0 に2Dテクスチャのテクセルロード 
texld r1, r0, s1    // 2DテクスチャのR成分のトーンをr1に読み込む
mov r0.r, r0.gggg   // 2DテクスチャのG成分をr0のRにコピー
texld r2, r0, s1    // 2DテクスチャのG成分のトーンをr2に読み込む
mov r0.r, r0.bbbb   // 2DテクスチャのB成分をr0のRにコピー
texld r0, r0, s1    // 2DテクスチャのB成分のトーンをr0に読み込む
mov r0.r, r1        // r1に保持しておいたR成分のトーンをr0.rにコピー
mov r0.g, r2        // r2に保持しておいたG成分のトーンをr0.gにコピー
mul r0, r0, v0      // r0を頂点の色(1,1,1,a) と混合
mov oC0, r0         // 出力レジスタに色を書き出す

実際、ベンチマークをとってみると Cgの吐き出したコードのほうが重くなっている。謎の定数c0がらみのコードがなくなるだけで効率は同等になるっぽいんだが・・・

最適化があまいとかそういうレベルじゃなくて、脈絡のない無駄なコードが挿入されてしまっている。不思議だ。それとも何か僕がとんでもないミスをしてるんだろうか?

Cg の最適化(2)

Cg の吐き出すコードの件だが、どうも Sampler1Dを使っているのが原因のようだ。 Sampler1Dでもアセンブルされたコードでは2Dのテクスチャとして扱われているが、このときy座標を中央の0.5にしてしまうのが原因ってことかな?
どうせ1Dテクスチャのy座標はどんな値でも関係ない状態なので、以下のように意図的に座標を無視するようにSampler2Dを使ってみた。
void FragmentProgram(float4 color : COLOR0,
  in float2 texCoord0 : TEXCOORD0,
  out float4 colorO : COLOR0,
  const uniform sampler2D PictureTexture,
  const uniform sampler2D ToneCurveTexture)
{
  float4 texCol = tex2D(PictureTexture, texCoord0);
  colorO.r = tex2D(ToneCurveTexture, texCol.rr).r;
  colorO.g = tex2D(ToneCurveTexture, texCol.gg).g;
  colorO.b = tex2D(ToneCurveTexture, texCol.bb).b;
  colorO.a = texCol.a * color.a;
}


これをCgでコンパイルすると生成されるコードは以下のようになって、だいぶ効率がよくなった。
ps_2_0
dcl_2d s0
dcl_2d s1
dcl t0.xy
dcl v0.xyzw
texld r0, t0, s0
mov r1, r0.z
texld r1, r1, s1
mov r0.z, r1
mov r1, r0.y
texld r1, r1, s1
mov r0.y, r1
mov r1, r0.x
texld r1, r1, s1
mov r0.x, r1
mul r0.w, r0, v0
mov oC0, r0


ちなみに同じコードを DirectX9 のHLSLコンパイラ に吐き出させるとこんな感じ。(CgとHLSLの言語仕様が同じってのは助かるねえ!)
ps_2_0
dcl v0
dcl t0.xy
dcl_2d s0
dcl_2d s1
texld r1, t0, s0
mov r2.xy, r1.x
mov r1.x, r1.y
mov r0.xy, r1.z
texld r3, r2, s1
texld r2, r1, s1
texld r0, r0, s1
mov r0.x, r3.x
mov r0.y, r2.y
mul r0.w, r1.w, v0.w
mov oC0, r0
うーむ、結構ちがうな。Cgより mov命令が一つ少なくなってる。HLSLのほうが賢いんかなあ...
どのみち Cgのランタイムを配布するのは面倒なので、Hamanaに使うのはHLSLにしたほうが良さそうだ。

1/1