Shuhei Kagawa

Ruby の Curses で Hello, World!

Oct 16, 2009

以前から wget の進行状況表示など、コマンドラインでアニメーションを表示するにはどうやってやるのか気になっていました。 sl コマンド のソースを覗くと curse.h なるものを使っています。Ruby でもできるのかな?と思い、やってみました。 参考にしたのは Ruby の Curses を使ってコンソールを制御する(1/2):CodeZinecurses – Ruby リファレンスマニュアル

require 'curses'

message = [
  "**   **  *******  **       **        *****    **",
  "**   **  **       **       **       **   **   **",
  "*******  *******  **       **       **   **   **",
  "**   **  **       **       **       **   **     ",
  "**   **  *******  *******  *******   *****    **"
]

def animate(image)
  include Curses
  init_screen
  y = ((lines - image.size) / 2).truncate
  (cols - image[0].size).downto(1) do |t|
    image.size.times do |i|
      setpos(y + i, t)
      addstr(image[i])
    end
    refresh
    sleep(0.05)
    clear
  end
close_screen
end

animate(message)

おおー、動きました。逆に新鮮で面白いですね。

NTLM ハッシュ を求める

Oct 13, 2009 - Ruby

最近の Windows のパスワードの暗号化に使われているそうです。元の文字列をリトルエンディアンの UTF-16 に符号化したものから MD4 でハッシュを作成するのだとか。

Ruby で

16 進数表現の文字列として出力します。もう少しうまい方法がある気がするのですが・・・。 NTLM ハッシュの計算そのものは Ruby/NTLM に頼っています。ちなみに Ruby/NTLM をインストールするには、 gem install rubyntlm とします。

require 'rubygems'
require 'net/ntlm'

def ntlm_hash16(password)
  result = ""
  hash = Net::NTLM.ntlm_hash(password)
  hash.size.times do |i|
    result += sprintf("%02x", hash[i])
  end
  result
end

ntlm_hash16("p@ssw0rd") #=> "de26cce0356891a4a020e7c4957afc72"

追記

sprintf("%02x", hash[i]) の部分を、最初は hash[i].to_s(16) と書いていました。しかし、これだと 15 以下の数が来たときに 0 が抜けてしまうので、修正しました。

Python で

ついでに Python でも。

import hashlib

def ntlm_hash16(password):
  result = ""
  digest = hashlib.new('md4', password.encode('utf-16le')).digest()
  for ch in digest:
    result += "%02x" % ord(ch)
  return result

ntlm_hash16("p@ssw0rd") #=> 'de26cce0356891a4a020e7c4957afc72'

Ruby でも Python でも、さすがに結果は同じようですね。

追記

Python でも Ruby と同じような間違いをしていました。 "%02x" % ord(ch) の部分を、最初は hex(ord(ch))[2:] と書いていました。 いやあ、テストは大事ですね。

Python Challenge Lv. 8 - 9

Oct 12, 2009 - Ruby, Python

Level 8

とりあえず <area> タグの coords 属性を描いてみました。Ruby と 苦労してインストールした cairo を使用。

require 'cairo'

nums = [179,284,214,311,255,320,281,226,319,224,363,309,339,222,371,225,411,229,404,242,415,252,428,233,428,214,394,207,383,205,390,195,423,192,439,193,442,209,440,215,450,221,457,226,469,202,475,187,494,188,494,169,498,147,491,121,477,136,481,96,471,94,458,98,444,91,420,87,405,92,391,88,376,82,350,79,330,82,314,85,305,90,299,96,290,103,276,110,262,114,225,123,212,125,185,133,138,144,118,160,97,168,87,176,110,180,145,176,153,176,150,182,137,190,126,194,121,198,126,203,151,205,160,195,168,217,169,234,170,260,174,282]
points = []
while nums.size > 0 do
  points.push nums.slice!(0, 2)
end

def draw_points(points, width, height, filename)
  format = Cairo::FORMAT_ARGB32
  surface = Cairo::ImageSurface.new(format, width, height)
  context = Cairo::Context.new(surface)

  context.set_source_rgb(1, 1, 1)
  context.rectangle(0, 0, width, height)
  context.fill

  context.set_source_rgb(0, 0, 0)
  context.move_to(points[0][0], points[0][1])
  points.each do |p|
    context.line_to p[0], p[1]
  end
  context.stroke

  surface.write_to_png(filename)
end

draw_points(points, 640, 480, "fly.png")

しかし、ただの蠅の輪郭です。リンク部分の形ですね。で、今度は un 文字列を座標に見立てて線を引いてみたりしたのですが・・・。

un = "BZh91AY&SYA\xaf\x82\r\x00\x00\x01\x01\x80\x02\xc0\x02\x00 \x00!\x9ah3M\x07<]\xc9\x14\xe1BA\x06\xbe\x084"
un_points = []
0.step(un.size - 1, 2) do |i|
  un_points.push([un[i], un[i + 1]])
end

draw_points(points, 640, 480, "un.png")

全然違うようです・・・。こりゃあかんということで、フォーラムにヒントを見に行くと、Python のモジュールを探せ、un と pw の形式をよく見ろとのこと。共通する「BZh91AY&S」でググってみたところ、bzip2 のヘッダ部分だとか。知らんがな。ということで、Python の bz2 モジュールで解凍して答えを得ました。

>>> import bz2
>>> un = "BZh91AY&SYA\xaf\x82\r\x00\x00\x01\x01\x80\x02\xc0\x02\x00 \x00!\x9ah3M\x07<]\xc9\x14\xe1BA\x06\xbe\x084"
>>> pw = "BZh91AY&SY\x94$|\x0e\x00\x00\x00\x81\x00\x03$ \x00!\x9ah3M\x13<]\xc9\x14\xe1BBP\x91\xf08"
>>> bz2.decompress(pw)
'file'
>>> bz2.decompress(un)
'huge'

Level 9

Level 7 でやってみたのは、こっちの解法だったようです。 ということで、以下。

require 'cairo'

first = [略]
second = [略]

first_points = []
while first.size > 0 do
   first_points.push first.slice!(0, 2)
end

second_points = []
while second.size > 0 do
   second_points.push second.slice!(0, 2)
end

draw_points(first_points, 495, 495, "first.png")
draw_points(second_points, 495, 495, "second.png")

first

ということで、出てきた動物の名前をいくつか適当に入れると正解に。しかし、まだまだ四分の一。先は長いです・・・。