著作一覧 |
ActiveScriptのエンコーダについて調べた。
screncというツールを使って、コメント内に**Script Encode**を埋め込んだVBScriptやJScriptをエンコードする仕組みだ。
エンコードしたスクリプトは、通常のVBScriptではなく、VBScript.Encodeというクラス名で作成できるActiveScriptエンジンが処理する。
screncでエンコードした結果を見ると、quoted-printable encodingされた文字列に見える。といっても、デコードしても良くわからないバイナリーとなる。多分、キーワードを固定辞書でバイナリ値に置き換えているのではないだろうか。いずれにしろ、暗号化しているわけではないというような説明がされている。
なんの役に立つかと言うと、難読化と一種のハッシュ埋め込み(字が変わればデコードできないので安心)ということらしい。
いかにもありそうではあるが、脆弱性も見つかったりしている。
それはそれとして、Rubyで似たような仕組みを考えてみる。難読化とハッシュ。デコードして書き変えて再エンコードを防ぐ必要はないということ。だったら圧縮でバイナリにして、それをBase64でエンコードすれば良さそうだ。
こんな感じ。
c:\temp>type hello.rb ← 元のスクリプト #**Start Encode** puts("what your name ?") name = gets puts("hello #{name.chop} !") c:\temp>ruby screnc.rb hello.rb hello2.rb ← エンコーダでエンコード c:\temp>type hello2.rb ← エンコードしたスクリプト #**Start Encode**#@~eJzjKigtKdZQKs9ILFGozC8tUshLzE1VsFfS5AIzbBXSU0uKuSCKMlJzcvIV lKtBMnrJGfkFtQqKQIUA7ikViA== ~@ c:\home\arton\temp>ruby scrdec.rb hello2.rb ← デコーダで実行 what your name ? anonymous ← getsがARGFになるので最初びっくりした。 hello anonymous !
エンコーダはこんな感じで作ってみた。
require 'zlib' if ARGV.size != 2 || !File.exist?(ARGV[0]) $stderr.puts("usage: ruby screnc.rb src dst") else File.open(ARGV[1], 'w') do |dst| s = File::read(ARGV[0]) if s =~ /\#\*\*Start Encode\*\*(.*)$\Z/m dst.write($`) dst.write("#**Start Encode**") dst.write("#\@~") dst.write([Zlib::Deflate.deflate($1)].pack('m')) dst.write("~\@") else dst.write(s) end end end
デコーダはこんな感じかな。
require 'tempfile' require 'zlib' alias _org_require require def scr_require(scr) s = File::read(scr) if s =~ /\#\*\*Start Encode\*\*\#\@\~(.*)\~\@/m tmpf = Tempfile.new('scrf') begin tmpf.write($`) tmpf.write(Zlib::Inflate.inflate($1.unpack('m')[0])) tmpf.close(false) File.rename(tmpf.path, "#{tmpf.path}.rb") _org_require(tmpf.path) ensure File.delete("#{tmpf.path}.rb") end else _org_require(scr) end end def require(s) if File.exist?("#{s}.rb") scr_require("#{s}.rb") elsif File.exist?(s) scr_require(s) else _org_require(s) end end if $0 == __FILE__ if ARGV.size != 1 || !File.exist?(ARGV[0]) $stderr.puts("usage: ruby scrdec.rb encoded") else def gets $stdin.gets end require ARGV[0] end end
残念ながら、木村さんが間違っているのだが、何しろ、VB6以前はひげ(.NET)の世界より昔なので、土に埋もれてなかなか見つからない。それともVB.NETの話をしているのだろうか?
とりあえず、XLソフトの資料が見つかったので、リンクしとこう。
Basic コード: Declare Sub FORTARRAY Lib "fortarr.dll" (Barray as Single) DIM barray (1 to 3, 1 to 7) As Single Call FORTARRAY(barray (1,1)) Fortran コード: Subroutine FORTARRAY(arr) REAL arr(3,7)
実際のところ、効率を無視すれば、どっちがどっちでも動くことは動くので、仕事プログラムでも逆に書いているのをたくさん見つけて頭を抱えたものだ(直さなかったけど。というか、VBプログラマは仕様を読まない。その代わりに、どこぞのCプログラマから聞かされた与太話を信じて仕事をするということはわかった)。このあたりは、SAFEARRAYの定義を読むとわかることはわかる(と思って読み返したらわからなかった)。
追記:やっぱりSAFEARRAYの仕様でわかるや。rgsabound単位にLboundが設定できるということと、rgsabound[0]がleft-most dimensionだということ、あるいは(同じことだけど)Redim(10)をRedim(10,10)に変えられること(.NETではできない)というように、各ランクが独立していることと、最初のランクが左端ということから、メモリ配置はランク単位に行われることになる。
ジェズイットを見習え |