読者です 読者をやめる 読者になる 読者になる

ppencode 2 - 任意の Perl コードを予約語だけの Perl コードに変換する

@TAKESAKO さんが ppencode を作ってから 10 年経って、私が任意の Perl コードを小文字だけに変換するスクリプトを書いてからでも5年経つらしいですが、なんか任意の Perl コードを予約語だけの Perl コードに変換するスクリプトができました。

http://shinh.skr.jp/obf/ppencode.html

オリジナルの ppencode に敬意を表していろいろ雰囲気を似せておきました。割と色んな予約語が使われるようにしてみたりとか。

これは Perl予約語だけあれば Turing complete ということなので、副産物として Quine もできました。

http://golf.shinh.org/reveal.rb?Quine/shinh+%28keywords%29_1431106882&pl

なんというか、 ppencode があれば後は print を eval に変えるだけちゃうん?という当然の疑問があると思うのですが、 ppencode より割と大変だったりします。 ppencode は要は

print chr 72 xor print chr 101 xor print chr 121  # Hey

みたいな感じで実現できて、数字の部分は length q q XXXX q とかしてやって XXXX の部分を q を含まないキーワードで適当に埋めてやれば OK です。

ただこれは文字数だけ print を呼べば良いからできる方法であって、 eval みたいに文字列を一旦まとめる必要があるとダメ。

小文字アルファベットしばり PerlBase64 しばり Perlでは s//something/e を最初に連打することによって、好きな文字列を作ることができましたー、というのがこれまでのあらすじ。

これを予約語だけでやろうとすると、

s XX chr length ????? XYYY xor ... xor eval

の形になるとして、 X は 1 文字の小文字アルファベット、 YYY は e を含む文字列で正規表現の modifier として valid で、かつ、 XX と XYYY は予約語でないといけない、という条件になります。 XX と同じ文字が並んでる予約語は qq しか無く、 q ではじまって e を含む唯一の予約語である quotemeta は t が modifier として不適切です。

ああ、詰んだな、と放置してました。今回気付いたのは、1回の正規表現置換で無理なら2回やれば無理かなーということで、

s qq q
s X X chr length ????? XYYY

の形、つまり最初に空白を足しておいてから、その空白を本命の文字に変えれば良いのではないかと。 X が連続してなくて良いので、 X の候補が m, q, s, x, y の5つに増えます。最初、 X = s, XYYY = sleep がうまくいきそうな気がしたんですが、 e が2つあると2回 eval されちゃって扱いにくいったらありゃしないのでもうちょっと眺めてると、 semop がありました。ありがとう POSIX 。まぁ splice とかも該当すると思いますが。

ここまで来るとあとはやるだけーと思ったのですが、空白を含んだスクリプトがうまく変換できません。一番最初にある空白を次の文字で入れ変えていくので、空白を詰めてしまうので。 print "Hey" だと

___________  # 最初に 11 文字空白を確保(_ で空白を示します)
p__________  # 1文字ずつ空白が置きかわっていく
pr_________
pri________
prin_______
print______
print______  # 空白を空白に置き変えてる
print"_____  # 詰められてしまった
print"H____
print"He___
print"Hey__
print"Hey"_

というように変換されていく感じ。 JS コード中の ppencode_core がこの変換をしてます。

こういう場合はまあ、空白が無い、評価すると空白を含んだ Perl コードになる Perl コードを作って、 2回 eval すれば良い…とのことで例のごとく version string に活躍してもらいました。

print "Hey" だと

$_="";v112.114.105.110.116.32.34.72.101.121.34

というコードを ppencode_core でエンコードして、これを2回 eval すれば良い、と。最初の $_=""; は $_ をいじってるとあまり厳密な変換じゃないな…と思ったからつけただけです。

長い間、「普通にできんじゃねーの?」→「あれ、できないな。。」を定期的に繰り返してたので解決して良かったと言えます

なにかあれば下記メールアドレスへ。
shinichiro.hamaji _at_ gmail.com
shinichiro.h