難読化されたTeXの解読(tkbctf3 Misc 100 Real World TeX)

tkbctf3で出題された問題。

問題

^^5c^^66^^75^^74^^75^^72^^65^^6c^^65^^74^^7e ^^5c^^63^^61^^74^^63^^6f^^64^^65^^60K7
KK5cKK65KK6eKK64KK6cKK69KK6eKK65KK63KK68KK61KK72- KK5cKK73KK74KK72KK69KK6eKK67KK60
KK7eKK60I13KK7eKK60G10KK5cKK6cKK65KK74 IKK7eI86G10I83G7I72G1I90V0I78V2I80G6I82G5
I13G9I76G13ZKK6cKK65KK74GLZKK6cKK65KK74I65G13LZSS46ZKK66SS6fKK6eKK74VLZSS54ZSS6dSS61SS67KK73KK74SS65KK70V
ZSS46ZSS62KK69SS67KK3dSS63KK6dKK7210VKK73SS63SS61KK6cKK65SS64VZSS6dKK61KK67KK73KK74SS65KK704V
ZKK46ZKK73SS63KK3dSS63KK6dKK63KK73SS6310VKK73KK63SS61SS6cSS65KK64VZKK6dKK61SS67SS73KK74KK65KK704V
ZSS46ZKK62SS66SS3dSS63SS6dSS62SS7810ZKK72KK65KK6cKK61SS78V
ZKK64SS65SS66ZKK68SS6fP1P2HP2P1NVLKK41ZSS68SS6fV
KK60KK60HZKK62SS69KK67VSS4dNSS6fSS73SS74VKK70KK72KK6fKK67KK72SS61SS6dKK6dKK69KK6eSS67V
SS6cSS61KK6eKK67KK75KK61KK67SS65SS73VKK61SS72KK65VSS70KK61KK72SS74SS6cSS79VSS61VKK77KK61SS79VSS6fSS66V
SS65SS78SS70KK72SS65SS73KK73SS69SS6eKK67VSS74SS68KK69KK6eKK67SS73VKK69KK6eVV
SS74SS65SS72SS6dKK73VSS6fSS66VVSS6fSS74KK68SS65KK72VKK74SS68KK69SS6eSS67SS73V
KK61KK6eKK64VSS70SS61SS72KK74SS6cSS79VVSS61VSS62SS61SS73KK69SS63VSS73KK65SS74V
SS6fSS66VKK67SS69SS76SS65SS6eVKK74SS68SS69KK6eKK67SS73SS2eKK27KK27VZKK76SS73KK6bSS69SS70V10KK70KK74V
HZSS73KK63ZSS63KK61SS74KK63SS6fKK64KK65KK60SS4912VKK41SS73KK49SS41KK69SS6dKK6dNVKK69SS73VV
KK74KK68KK65VHZKK62KK66VSS6bSS65SS79SS77KK6fKK72KK64NVSS6fKK66VSS74KK68KK69KK73VSS71KK75KK65KK73SS74KK69KK6fKK6eKK21V
ZKK6eSS65SS77SS77SS72KK69SS74SS65ZSS61ZKK72SS65KK6cSS61SS78V
ZSS6fKK70KK65KK6eSS6fSS75KK74ZSS61KK3dZSS6aKK6fSS62KK6eSS61SS6dKK65SS2eKK69KK64KK78ZSS72KK65SS6cSS61KK78V
ZKK77KK72KK69KK74KK65ZKK61HKK41SS61SS4cKK6eKK411SS64SS6eVSS69KK73VSS61KK6eSS6fKK74SS68KK65SS72VKK6bKK65SS79SS77KK6fKK72KK64KK21NV
ZKK63KK6cSS6fKK73KK65SS6fSS75SS74ZSS61ZSS72SS65SS6cKK61KK78VZKK62SS79KK65

実はこれは正しいTeXファイルで、先頭に

\documentclass{article}
\begin{document}

を、末尾に

\end{document}

を追加するとコンパイルできて、1個目のフラグが表示される。(ファイル名).idxに2個目のフラグが出力される。
どのようにコンパイルされているのか辿ってみたら面白かった。

1行目

TeXでは^^nnは文字コード0xnnの文字になる。これで1行目が解読できる。

\futurelet~ \catcode`K7

\futureletが良く分からないけど、まず\catcode`K7が実行され、以降に~ A Bが出てくると\catcode A Bが実行されるらしい。\catcodeは\catcode `K 7でKのカテゴリーコードを7にする。`Kの代わりに、\catcode 75 7と、文字コードを10進数で書くこともできる。カテゴリーコードはここに載っている。\catcode`K7によって、以降Kは^と同じ働きをするようになる。よって、KKnnは文字コード0xnnの文字になる。

\futurelet~ \catcode`K7
\endlinechar- \string`
~`I13~`G10\let I~I86G10I83G7I72G1I90V0I78V2I80G6I82G5
I13G9I76G13ZletGLZletI65G13LZSS46ZfSS6fntVLZSS54ZSS6dSS61SS67stSS65pV
ZSS46ZSS62iSS67=SS63mr10VsSS63SS61leSS64VZSS6dagstSS65p4V
ZFZsSS63=SS63mcsSS6310VscSS61SS6cSS65dVZmaSS67SS73tep4V
ZSS46ZbSS66SS3dSS63SS6dSS62SS7810ZrelaSS78V
ZdSS65SS66ZhSS6fP1P2HP2P1NVLAZSS68SS6fV
``HZbSS69gVSS4dNSS6fSS73SS74VprogrSS61SS6dminSS67V
SS6cSS61nguagSS65SS73VaSS72eVSS70arSS74SS6cSS79VSS61VwaSS79VSS6fSS66V
SS65SS78SS70rSS65SS73sSS69SS6egVSS74SS68ingSS73VinVV
SS74SS65SS72SS6dsVSS6fSS66VVSS6fSS74hSS65rVtSS68iSS6eSS67SS73V
andVSS70SS61SS72tSS6cSS79VVSS61VSS62SS61SS73iSS63VSS73eSS74V
SS6fSS66VgSS69SS76SS65SS6eVtSS68SS69ngSS73SS2e''VZvSS73kSS69SS70V10ptV
HZSS73cZSS63aSS74cSS6fde`SS4912VASS73ISS41iSS6dmNViSS73VV
theVHZbfVSS6bSS65SS79SS77ordNVSS6ffVSS74hisVSS71uesSS74ion!V
ZnSS65SS77SS77SS72iSS74SS65ZSS61ZrSS65lSS61SS78V
ZSS6fpenSS6fSS75tZSS61=ZSS6aoSS62nSS61SS6deSS2eidxZSS72eSS6cSS61xV
ZwriteZaHASS61SS4cnA1SS64SS6eVSS69sVSS61nSS6ftSS68eSS72VkeSS79SS77ord!NV
ZclSS6fseSS6fSS75SS74ZSS61ZSS72SS65SS6caxVZbSS79e

2行目

\endlinechar- \string`

は分からない。コメントアウトしても動くから、難読化には関係なさそう。

3行目(と4行目の一部)

~`I13~`G10

~が\catcodeになっているので、これは、

\catcode `I 13
\catcode `G 10

と等価。Iのカテゴリコードを~と同じに、Gのカテゴリコードを空白と同じにしている。Iのカテゴリコードを英文字から変えたことで、\Iではなく、Iだけでコマンドとして使えるようになる。

\let I~

Iに~を代入。今、~は\catcodeなので、I A Bと書くと、\catcode A Bになる。

I86G10I83G7I72G1I90V0I78V2I80G6I82G5
I13G9I76G13

Iが\catcodeでGが空白文字なので、

\catcode 86 10 % V → [ ]
\catcode 83 7 % S → ^
\catcode 72 1 % H → {
\catcode 90 0 % Z → \
\catcode 78 2 % N → }
\catcode 80 6 % P → #
\catcode 82 5 % R → \n
\catcode 13 9 % \r → \0
\catcode 76 13 % L → ~

になる。
Sが^なので、以降、SSnnは0xnnになる。

\futurelet~ \catcode`K7
\endlinechar- \string`
~`I13~`G10\let I~I86G10I83G7I72G1I90V0I78V2I80G6I82G5
I13G9I76G13ZletGLZletI65G13LZFZfontVLZTZmagstepV
ZFZbig=cmr10VscaledVZmagstep4V
ZFZsc=cmcsc10VscaledVZmagstep4V
ZFZbf=cmbx10ZrelaxV
ZdefZhoP1P2HP2P1NVLAZhoV
``HZbigVMNostVprogrammingV
languagesVareVpartlyVaVwayVofV
expressingVthingsVinVV
termsVofVVotherVthingsV
andVpartlyVVaVbasicVsetV
ofVgivenVthings.''VZvskipV10ptV
HZscZcatcode`I12VAsIAimmNVisVV
theVHZbfVkeywordNVofVthisVquestion!V
ZnewwriteZaZrelaxV
ZopenoutZa=Zjobname.idxZrelaxV
ZwriteZaHAaLnA1dnVisVanotherVkeyword!NV
ZcloseoutZaZrelaxVZbye

4行目

ZletGLZlet

 ↓

\let L \let

Lが\letになる。

I65G13

 ↓

\catcode 65 13 % A → ~

あとで、\let A hogeをするため。

LZFZfontVLZTZmagstepV

 ↓

\let \F \font
\let \T \magstep

5-7行目

ZFZbig=cmr10VscaledVZmagstep4V
ZFZsc=cmcsc10VscaledVZmagstep4V
ZFZbf=cmbx10ZrelaxV

 ↓

\F\big=cmr10 scaled \magstep4
\F\sc=cmcsc10 scaled \magstep4 
\F\bf=cmbx10 \relax

8行目

ZdefZhoP1P2HP2P1NVLAZhoV

 ↓

\def \ho #1#2 {#2#1}
\let A \ho

直後の文字を入れ替えるコマンドAを定義している。

9行目以降

``HZbigVMNostVprogrammingV
languagesVareVpartlyVaVwayVofV
expressingVthingsVinVV
termsVofVVotherVthingsV
andVpartlyVVaVbasicVsetV
ofVgivenVthings.''VZvskipV10ptV
HZscZcatcode`I12VAsIAimmNVisVV
theVHZbfVkeywordNVofVthisVquestion!V
ZnewwriteZaZrelaxV
ZopenoutZa=Zjobname.idxZrelaxV
ZwriteZaHAaLnA1dnVisVanotherVkeyword!NV
ZcloseoutZaZrelaxVZbye

 ↓

``{\big M}ost programming 
languages are partly a way of 
expressing things in  
terms of  other things 
and partly  a basic set 
of given things.'' \vskip 10pt 
{\sc\catcode`I12 AsIAimm} is  
the {\bf keyword} of this question! 
\newwrite\a\relax 
\openout\a=\jobname.idx\relax 
\write\a{AaLnA1dn is another keyword!} 
\closeout\a\relax \bye

\catcode`I12でIが英文字に戻り、コマンドAをによって直後の文字が入れ替わるので、

{\sc Ismim}
{Land1n is another keyword!}