Rubyでステガノグラフィーを作ってみた
概要
この記事の対象者:
Ruby初心者やRubyKaigiに興味がある方
奇妙なコードやステガノグラフィー技術に興味がある方
この記事の内容:
RubyKaigi2024の紹介
ステガノグラフィー技術を用いたコードの例
この記事を読むとわかること:
「Writing Weird Code」の講演内容
実際にステガノグラフィー画像を作成する手順
序章
みなさん、Rubyはお好きでしょうか?
私は、先日まで宝石のRubyも言語のRubyも触ったことがありませんでした。
しかし5/15~17に那覇で開催されたRubyKaigi2024に参加し、
Rubyの面白さ(言語の方)に触れることができました。
今日はその紹介をしたいと思います。
Writing Weird Code
RubyKaigiでは様々な講演がありましたが、特に印象に残っているのは
tompngさんの「Writing Weird Code(奇妙なコードを書く)」です。
コードはできるだけみやすく書くのが原則だと思います。
しかしこの講演は逆でいかに奇妙なコードを書き、その奇妙さや芸術性(!?)を楽しむものでした。
著者のtompngさんは超絶技巧Ruby意味不明コンテスト2022で金メダルを取られたプログラマーです。
奇妙なコードを書くメリットとして、アートの感性が得られるだけでなく
なかなか見つからなかったバグの発見にも繋がるらしいです。
また、超絶技巧Ruby意味不明コンテストは不定期で開催され2024年はなかったそうです。
そこで自分一人で超絶技巧Ruby意味不明コンテスト2024を開かれ6つのコードを紹介されていました。
1つ1つ紹介するたび会場は大いに盛り上がり、さすが奇妙な人(褒め言葉)だと感じました。
特に私が心に刺さったのはMost floral部門のコードです。
言葉だけだと伝わりづらいのですが、具体的には一見美しい花の画像がありました。
しかし、これをRubyで実行すると、画像からアスキーアートのアニメーションが飛び出してくるのです。
(なんとも奇妙ですねー・・・)
ステガノグラフィーの仕組み
このような現象の背後には、
画像内にコードを隠蔽するステガノグラフィー技術が使われています。
オリジナルのPNG画像のデータを微妙に改変し、新たなBMP画像として出力することで、見た目には普通の画像に見えるのに、その中にRubyコードが埋め込まれているのです。
具体的には、このBMPファイル内のRGB値の下位2ビット分にRubyコードを埋め込んでいます。
下位2ビットの違いは人間の目にはほとんど分からないため、
見た目の色を変えずにコードを埋め込むことができるのです。
col = (color >> (8 * (cidx + 1))) & 0xff
((col & 0b11111100) | bits.shift(1).join.to_i(2)).chr
1行目のcolはRGB値を8ビットに変換しています。
2行目で下位2ビットを一旦クリアし、Rubyのコード情報2ビット分に置き換えています。
自分で作ってみた
このような奇妙なコードを見るとどうもやってみたいという気持ちに襲われてしまい、
作ってみました♡
こちら「ちゅらデータ」のロゴ画像ですが、ruby out.bmp
を実行すると
なんとRubyのアイコンと「ちゅらデータ」の文字のアニメーションが出てきます!
実際に作ったコードは以下にあります。
作り方
- オリジナルの画像をPNGファイルで保存します。
今回はinput.pngとしました。
- 表示したい文字を決め、SVGファイルを作成します。
今回はfont.svgとし、XY座標を考え1文字1文字作成しました。
(適切なSVGファイル作成方法がありましたら教えて欲しいです!)
今回は「CHURADATA RUBY」としたいので、「C、H、U、R、A、D、T、B、Y」の9文字を作成します。
<g class="H">
<path d="M0,0 L0,7 L1,7 L1,0z" />
<path d="M4,0 L4,7 L5,7 L5,0z" />
<path d="M1,4 L4,4 L4,3 L1,3z" />
</g>
M(Moveto)は新しい描画位置にペンを移動を意味します。
例: M0,0は、ペンを座標(0,0)に移動します。
L (lineto): 現在の位置から指定した位置まで直線を意味します。
例: L0,7は、現在の位置から座標(0,7)まで直線を引きます。
つまりM0,0 L0,7 L1,7 L1,0z
は(0,0)から始まり(0,7)→(1,7)→(1,0)となる
逆時計回りの長方形を描いています。
同様に上から2番目のpathM4,0 L4,7 L5,7 L5,0z
は右サイドの縦棒で
上から3つ目のpathM1,4 L4,4 L4,3 L1,3z
は横棒を描画しています。
3. ruby font.rb
を実行し、文字コードを取得します。
こちらは先ほど作ったパスデータを数値(座標)に変換するコードです。
次に実行する3dグラフィックで扱えるようにデータの形状を変換してやります。
4. code.rb
内に、先ほど実行で取得した文字コード部分を埋め込みます。
例えば、Hの場合は以下の通りです。
FONT={
H:[[[0,0],[2,0],[0,14]]...
-
code.rb
に、任意のアニメーションを追加します。
今回はRubyロゴ(菩薩になった気持ちで見てください)がだんだん小さくなるアニメーションを追加します。
アニメーションは以下のように実装しました
# 三角形の定義
def inverted_triangle(scale_factor)
vertices = [
[-base_size, 0, y_offset],
[base_size, 0, y_offset],
[0, height, y_offset] # 上の頂点
]
triangles = [
[vertices[0], vertices[1], vertices[2]]
]
triangles
end
# アニメーションの定義
if t < 3
scale_factor = 10 - t * 4
elsif t >= 2
scale_factor = 0
end
inverted_triangle(scale_factor).each do |tri|
c.triangle(*tri)
end
-
ruby code.rb
を実行しイメージ通りのアニメーションができているか確認します。
イメージ通りにできていなければ、code.rb
を変更し再確認します。 - 最後に、
ruby generator.rb
でout.bmp
ファイルを出力すれば完成です!!!!
結論
今まで友達にあぶり絵を渡していたのですが、今度からはこのBMP形式で渡そうと思います。
脱線
今回紹介しましたtompngさんは、私が社会人初の名刺を渡した方になります。
「奇妙なコードを書く」著者に奇妙な出会いができ、こうして奇妙な記事を書くことができました。
入社したばかりの私にも、会社からRubyKaigiに参加するチャンスを頂けたり、
tompngさんなど先輩から多くのRubyistを紹介してもらえたりして、とても嬉しかったです。
「ちゅらデータ」に興味ある方いましたらお気軽にお声掛けください!
参照
tompngさんの紹介されていたリポジトリはこちらです
Discussion