本稿はTeX & LaTeX Advent Calendar 2016の21日目の記事です。20日目はVoDさんでした。22日目は__rinxさんです。
TeX言語はしばしば「仕様が非直感的で難解」だと言われていますが、これはおそらく大量に登場するバックスラッシュ\や、展開制御をはじめとするTeX特有の概念に慣れている人が少ないことに起因するものです。TeX言語そのものが本質的に難解で、どんなTeXコードでも一様に可読性が低いということはありません。実際、TeXが難解言語でないことは民主的にも示されています。
課題の要件に「難解プログラミング言語(esoteric language)はやめろ」と書いてあることに気付いた。#TeX言語 は上記に該当するだろうか?
— ワトソン (@wtsnjp) 2016年10月2日
本稿では、そんなTeX言語のコードをホンキで読みにくくすることを考えます。
目的と方針
一般にコードを難読化する動機には次のようなものがあります。
- リバースエンジニアリングの防止
- 作成あるいは解読するという娯楽
しかし、TeX言語の場合は1.のように実用的な目的で難読化を行うことは考えにくいです。したがって、TeX言語の難読化はほぼ娯楽目的に限られると言えるでしょう1。
逆に言うと、本稿で紹介する難読化のテクニックはまったくもって実用的でないとも言えます。間違っても、通常のLaTeX文書等においてこれらの手法を使用するべきではありません。
このことを明確にするため、本稿で示すコードはすべて(実用的に利用している人が少ないだろう)plain TeXを前提とすることにします。
難読化テクニック
コードの難読化に用いられるテクニックは以下の2つに大別できます。
- ロジックやデータ構造を難解にする
- 可読性を落とすのに有用な言語仕様を活用する
このうち1.については難読化したいコードの内容によってケース・バイ・ケースということになるので、本稿のような概説的記事で紹介するには向きません。そこで、ここでは難読化に役立ちそうなTeX言語のアレな仕様を順々に紹介していこうと思います2。
マクロ定義
TeXといえばマクロ、マクロ定義といえば\defプリミティブですが、この命令はかなり自由奔放な性格をしています。通常はこの自由度の高さが便利な命令を定義するのに役立つわけですが、これを逆手に取ると極めて非直感的な仕様の命令を作成することもできてしまいます。
まずは\defを用いて普通のマクロを作成し、使ってみましょう。
\def\Iam#1{I am #1.}
\Iam{a boy}
\end

このように、普通のマクロでは{と}を用いて1つの引数の範囲を明示します。通常はこの{}を省略するとおかしな結果になってしまいます。
\def\Iam#1{I am #1.}
\Iam an artist
\end

しかし、これを回避する方法もあります。\Iam命令の定義を次のように変更します。
\def\Iam#1.{I am #1!}
\Iam an artist.
\end

今度はうまくいきました。新しい\Iam命令は、\Iam自身から.が現れるまでを1つの引数#1と解釈するように定義されているからです。
このような普通でない引数の取り方はパターンマッチなどと呼ばれ、LaTeXやそのパッケージで[foo]や(bar)を(主にオプション引数として)扱う命令を定義するのにも利用されていますが、あまり乱用すると可読性を落とすことになります。
特に、パターンマッチに利用した記号はTeXに飲み込まれて消えるので、このことを悪用するとかなり非直感的な動作をするマクロを組むことができてしまいます。
\def\Iam not #1.{I am #1!}
\Iam not a \TeX nician.
\end

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

カテゴリーコードの変更
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

サーカムフレックス・メソッド(TeXエスケープ)
サーカムフレックス^を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

既に紹介したように、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<.(???>__????<.????:`
発展編
ありがたいことに、こんなニッチな内容にもかかわらず本稿には一定の反響をいただいており、いくつか発展編とも呼べるブログ記事が登場しています。TeXをさらに難解にしたい方は、ぜひこれらの記事も参考にしてみましょう!
- 記号と数字だけでTeX言語する話 | マクロツイーター(2023-09-30公開)
- アスキーアートで書くカンタンTeX | Daiji256(2025-12-02公開)
主な更新履歴
- 2025-12-02: スタイルの調整。発展編として外部記事へのリンクを追加
-
これら2つの中間的な実例として、過去に難読化されたTeXコードに関する問題がCTF(セキュリティ系の技術コンテストの1つ)で出題されたことがあるようです。 ↩︎
-
本稿で行うのはあくまで“紹介”です。厳密かつ詳細な説明はThe TeXbookやTeX by Topicなど他の文献を参照してください。 ↩︎
-
<char>がコメント文字である場合など、バックスラッシュを省略できない場合もあります。 ↩︎ -
より厳密には2連続するサーカムフレックスに限らず「同一の2連続するカテゴリーコードが7の文字」です。 ↩︎
-
基本的なASCIIコードは0–127までの値しかとらないため、
<char>の文字コードの値次第で64が足されるのか引かれるのかは一意に定まります(cf. キャレット記法)。 ↩︎