TeX の難読化

2016-12-21 (updated: 2018-08-08)  #TeX 

本稿は TeX & LaTeX Advent Calendar 2016 の21日目の記事です.20日目は VoD さんでした.22日目は __rinx さんです.

TeX 言語はしばしば「仕様が非直感的で難解」だと言われていますが,これはおそらく大量に登場するバックスラッシュ\や,展開制御をはじめとする TeX 特有の概念に慣れている人が少ないことに起因するものです.TeX 言語そのものが本質的に難解で,どんな TeX コードでも一様に可読性が低いということはありません.実際,TeX が難解言語でないことは民主的にも示されています.

本稿では,そんな TeX 言語のコードをホンキで読みにくくすることを考えます.

目的と方針

一般にコードを難読化する動機には次のようなものがあります.

  1. リバースエンジニアリングの防止
  2. 作成あるいは解読するという娯楽

しかし,TeX 言語の場合は 1. のように実用的な目的で難読化を行うことは考えにくいです.したがって,TeX 言語の難読化はほぼ娯楽目的に限られると言えるでしょう1

逆に言うと,本稿で紹介する難読化のテクニックはまったくもって実用的でないとも言えます.間違っても,通常の LaTeX 文書等においてこれらの手法を使用するべきではありません

このことを明確にするため,本稿で示すコードはすべて(実用的に利用している人が少ないだろう)plain TeX を前提とすることにします.

難読化テクニック

コードの難読化に用いられるテクニックは以下の2つに大別できます.

  1. ロジックやデータ構造を難解にする
  2. 可読性を落とすのに有用な言語仕様を活用する

このうち 1. については難読化したいコードの内容によってケース・バイ・ケースということになるので,本稿のような概説的記事で紹介するには向きません.そこで,ここでは難読化に役立ちそうな TeX 言語のアレな仕様を順々に紹介していこうと思います2

マクロ定義

TeX といえばマクロ,マクロ定義といえば\defプリミティブですが,この命令はかなり自由奔放な性格をしています.通常はこの自由度の高さが便利な命令を定義するのに役立つわけですが,これを逆手に取ると極めて非直感的な仕様の命令を作成することもできてしまいます.

まずは\defを用いて普通のマクロを作成し,使ってみましょう.

\def\Iam#1{I am #1.}
\Iam{a boy}
\end

I am a boy.

このように,普通のマクロでは{}を用いて1つの引数の範囲を明示します.通常はこの{}を省略するとおかしな結果になってしまいます.

\def\Iam#1{I am #1.}
\Iam an artist
\end

I am a.n artist.

しかし,これを回避する方法もあります.\Iam命令の定義を次のように変更します.

\def\Iam#1.{I am #1!}
\Iam an artist.
\end

I am an artist!

今度はうまくいきました.新しい\Iam命令は,\Iam自身から.が現れるまでを1つの引数#1と解釈するように定義されているからです.

このような普通でない引数の取り方はパターンマッチなどと呼ばれ,LaTeX やそのパッケージで[foo](bar)を(主にオプション引数として)扱う命令を定義するのにも利用されていますが,あまり乱用すると可読性を落とすことになります.

特に,パターンマッチに利用した記号は TeX に飲み込まれて消えるので,このことを悪用するとかなり非直感的な動作をするマクロを組むことができてしまいます.

\def\Iam not #1.{I am #1!}
\Iam not a \TeX nician.
\end

I am a TeXnician!

加えて「\def命令は既存の命令を上書きする」というよく知られた性質も難読化に役立ちます.\def命令が上書きできる “既存の命令” には特に制限がなく,たとえプリミティブであって容赦なく上書きされます.\def\defの定義を上書きすることすら可能です.

\def\def{def}\def
\end

def

カテゴリーコードの変更

TeX には制御綴を作る文字(一般的には\)や数式モードを開始する文字(一般的には$)など文字によってさまざまな “役割” が与えられています.こうした役割を規定しているのがカテゴリーコードで,基本的な TeX の処理系では次の表に示す16種類です.

カテゴリーコード 役割(種類) 通常この役割を担う文字
0 制御綴を開始 \
1 グループの開始 {
2 グループの終了 }
3 数式・非数式の切り替え $
4 アライメントの区切り &
5 行の終了 文字コード13の文字
6 パラメタ文字 #
7 上付き文字 ^
8 下付き文字 _
9 無視される 文字コード0の文字
10 空白文字 文字コード32の文字
11 英文字 A-Z, a-z
12 記号 0-9, !, ? ほか多数
13 アクティブ文字 ~
14 コメントの開始 %
15 無効文字 文字コード127の文字

このカテゴリーコードは TeX のソース中で変更することが可能です.その書式は以下の通りです.

\catcode`\<char>=<category code>

これにより<char>に記した文字のカテゴリーコードを<category code>に変更することができます.ただし,上記書式中の\=は省略することも可能です3

このことを利用すると,もはや TeX 言語には見えないようなコードを簡単に作ることができます.一例として,某エディタのあのコマンドを雑に実装してみました.

\catcode`:=0
\catcode`^=11
\def:smile{^\_^}

:smile

\end

smile

サーカムフレックス・メソッド

サーカムフレックス^を2つ並べた4後に文字<char>を書くと,TeX は^^<char>全体を<char>の文字コード(厳密には内部コードですが,ここでは ASCII コードだと思って問題ありません)から64増減5した文字コードにあたる文字が入力されたものとして解釈します.つまり,例えば^^jは文字*を,逆に^^*は文字jを表すということです.

TeX 言語のこの機能は(TeX が誕生した当時存在しただろう)入力可能な文字種が少ないキーボードでも TeX をフル活用できるようにするために用意されたもののようですが,これを多用すればどんな単純な TeX コードでも壊滅的な可読性を誇ることになります.

hello world!
\end

試しに上記の hello world コードをサーカムフレックス・メソッドを多用して表現してみましょう.

^^(^^%^^,^^,^^/^^`^^7^^/^^2^^,^^$^^a
\^^%^^.^^$

ただの hello world ですが,もはや見た目が Brainf*ck のそれと大差ありません.

アクティブ文字の活用

カテゴリーコードが13にあたる文字はアクティブ文字と呼ばれ,1文字で “命令” としての効果を持ちます.一般に~はカテゴリーコード13に属しており「改行不可の空白」を出力します.

しかし,このアクティブ文字の定義は\def命令を用いると,通常の制御綴と同じように変更することが可能です.

\def~#1{#1 is wonderful!}
~\TeX
\end

smile

既に紹介したように,TeX ではあらゆる文字のカテゴリーコードを自由に変更できるので,これら2つのテクニックを組み合わせると……もう,最悪です.

クイズ

ここまで,TeX コードを難読化するのに有用な TeX の仕様を色々と紹介してきました.最後に,これらのテクニックを用いて筆者から皆さんへのプレゼントを用意しました.ぜひ,TeX で実行する前にこのささやかなクリスマスプレゼントを受け取ってください.

             %%%(gMMp        %MNN&%%%
             %MMMMMMMp      %MMMMMMMD
               %MMMMMMp    %MMMMMMD`
                 %MMMMMp  %MMMMMD`          
            \^^#^^!^^4^^#^^/^^$^^%`W^^w
            \WW#WW!WW4WW#WW/WW$WW%%WWWw
`p^^q^^t paaJWWWWWWWWWWWWWWWWWWWWWWWWWW5aa.^^b^^c~
ppppppppfppfppfppfppfpNWWWWNppfppfppfppppppppppppp
ppfppfppfppfpfppfppfppWWWWWNppfppfppfpffpppppppfpf
ppfpfpfpppfppfpfpfppffWWWWWNpfpfppfppfppffpffpfppp
pfppfppffpfpfpppfpfppfNWWWWNppfpfpfpfppfppfppfpfpf
\^^#^^!^^4^^#^^/^^$^^%pWWWWN^^a^^&^^k^^v^^a^^!^^+`
`i^^p p???<~<???<~<???%WWWWW<!<????!?>???<~<???<~
 p(????<-+???<-+???<-(%WWWWW????-(????--????<-+?:
 i^^#^^!^^4^^#^^/^^$^^%%WWWW<^^X^^&^^9^^g^^\^^.^^
`?^^y ???p~<???<~<???WWWWWW<!<????!?>???<~<???<~}
 ???p~????<~<???<~<???WWWWWW<!<????!?>???<~<???<~
 p??++????++?????+??>?WWWWWW?++????++????<+????>_
 ????p?__+???<_(???<_(WWWNWW???>_(????--????<_+?:
 ???p???>?<<<???<<<>??WWWWWW<<<???><<????<<?>??<!
 WW)WW#WW!WW4WW#WW/WW$WW%%WWjWW@WW%WWUWW"WWLWWhWF
`WW:WWqWWsWW`%WWpWWGWW#WWW+WWBWWcWWRWW%WW{WW,WWW]
 WW)WW,WW%WW4WW:WW)TWW%X%WW:WWWqWWMWWEWW2WW*WW,WF
 EWW.WW*WW/WW9WW`WW9WW/WW5WW2WW`WW:WW-WW!WW3WWa%F
 iWW%WW.WW$pOTTTC1OTTTMMNMMMT1OTTTTzzTWWTCzTWWTC{
 ????%?:_+??><_<???<.<MMMMMM?>?>~(????~_????<_<?:
 p??<<??>?<<?????<????MMMMMM?<<????<<????<<????<:
 ??????%??<-+???<-(???MMMNMM+-(???>-(>???--+???<.
 ?%???><~<???<~<???<<<MMMMMM????<<????<~????<~<?:
 p+??>?<+????<+??>??+?MMMMMM????++???>++??>?++??:
 ??%_ ????<_+???<.(???MMMNMM<_(???>__????__+???<.
 ?<???><<>???<<<???<<<MMMMMM???><<?>??<<????<<<?:
 ?(????+(????+(????<(+MMMMMM?>??+(???>+(????+(??:
 ???p_???><.<???<.(???MMMNMM<.(???>__????<.????:`

  1. これら2つの中間的な実例として,過去に難読化された TeX コードに関する問題が CTF(セキュリティ系の技術コンテストの1つ)で出題されたことがあるようです. ↩︎

  2. 本稿で行うのはあくまで “紹介” です.厳密かつ詳細な説明は The TeXbookTeX by Topic など他の文献を参照してください. ↩︎

  3. <char>がコメント文字である場合など,バックスラッシュを省略できない場合もあります. ↩︎

  4. より厳密には2連続するサーカムフレックスに限らず「同一の2連続するカテゴリーコードが7の文字」です. ↩︎

  5. 基本的な ASCII コードは0-127までの値しかとらないため,<char>の文字コードの値次第で64が足されるのか引かれるのかは一意に定まります(cf. キャレット記法). ↩︎