難読化 (ソフトウェア)

ソフトウェアにおける難読化とは、コンピュータプログラムの動作を変えずに、プログラムコードの内部的なサブルーチンの内容・構造・データなどを、人間にとって読み取りにくくなるように改変・加工すること。難読化の対象はソースコードであったり、ソースコードから生成されるマシンコードまたはバイトコードなどの中間表現であったりすることもある。難読化されたコードは第三者によるプログラムの解読・解析が困難になる。

1. 概要
-----

おおよそ2つのいずれかの目的のため、プログラマのコーディング、専用アプリケーション、または開発ツールの補助機能によって、ソースコードや実行コードに対して故意にロジックやデータなどの難読化、曖昧化が施される。

コードの目的を隠蔽したり、改竄やリバースエンジニアリングを阻止したりするため。
コードを解読する者の腕試しのためのパズルや娯楽として。コード難読化はハードウェアの曖昧化とは本質的に異なっており、後者は電子回路の配置、構造を機能隠蔽を目的として改変することを意味する。

いくつかのプログラミング言語は、他の言語に比べ、その構造や性質により、難読化が容易なものもある。C言語、C++、Perlはその一例である。

一般に、ソフトウェアの実行にソースコードが必要となるスクリプト言語は解析が容易だが、コンパイラによって機械語を生成する言語は、そのコード変換過程の不可逆性により解析が難しい。機械語は単なる数値の羅列であり、逆アセンブル人間にとっていくらか分かりやすいニーモニックに変換すること。}}することは容易だが、解読には機械語やアセンブラの知識が必要となり、また元のソースコードを復元することは完全にはできないため、コードの意図まで読み取れるとは限らない。

リフレクションのサポートなどの目的で、コンパイル時にシンボル情報をメタデータとして保存する言語や処理系の場合、逆コンパイルによって元のソースコードを復元しやすいため、特にプロプライエタリな商用ソフトウェアでは、難読化によってリバースエンジニアリングを阻止することが多い。通常、プログラミングの際はサブルーチンや変数といったコード上のシンボルには人間にとって分かりやすい名前を付けるが、それらの名前が分かるだけでも解析がかなり容易になるため、そういったシンボルの名前を難読化ツールによって意味のないものに置換するだけでも一定の効果が望める。

2. 娯楽としての難読化
------------

難読化ソースコードの作成や解読は、頭の体操となる。いくつかコンテストもあり、創作性などを検討し表彰されるコンテストもある。例えば、国際難読化Cコードコンテスト、Obfuscated Perl Contestなどである。これらのコンテストはプログラムの働きの意外性などもコンテストのうちに含まれることも多く、難しさを競うコンテストではないし、単に難しいだけといった作品はまずない。

難読化のパターンには色々あり、単純なキーワード置換、スペースをうまく利用して芸術的な表現を行う、はたまた、自己生成やデータを高圧縮するものなど様々である。

Perlプログラマの中には署名欄のシグネチャに短く難読化されたPerlプログラムを組み込んでいるものもいる。このような署名にはJAPHsなどがある。

2.1. 例
------

これは1988年のIOCCCでエントリーされたコードである。作者はイアン・フィリップス、その後トーマス・ボールにより解読されている。

/* LEAST LIKELY TO COMPILE SUCCESSFULLY: Ian Phillipps, Cambridge Consultants Ltd., Cambridge, England

/
include main char

a; { return!

0<t? t<3?

main

+a)):

1, t<_? main

3,main &&t == 2 ?_ <13 ?

main 

9:16:t<0? t<-72? main( _, t, "@n'+,#'/*{}w+/w#cdnr/+,{}r/*de}+,/*{*+,/w{%+,/w#q#n+,/#{l,+,/n{n+,/+#n+,/#;\

q#n+,/+k#;*+,/'r :'d*'3,}{w+K w'K:'+}e#';dq#'l q#'+d'K#!/+k#;\q#'r}eKK#}w'r}eKK{nl]'/#;#q#n'){)#}w'){){nl]'/+#n';d}rw' i;# ){nl]!/n{n#'; \ r{#w'r nc{nl]'/#{l,+'K {rw' iK{;[{nl]'/w#q#\ \ n'wk nw' iwk{KK{nl]!/w{%'l##w#' i; :{nl]'/*{q#'ld;r'}{nlwb!/*de}'c ;;\ {nl'-{}rw]'/+,}##'*}#nc,',#nw]'/+kd'+e}+;\

'rdq#w! nr'/ ') }+}{rl#'{n' ')# }'+}##t<-50? _==*a ? putchar:

main

main + t, _, a + 1 )

0<t?

main 

a=='/'||main(0,

main-[w]*%n+r3#l,{}: uwloca-O;m .vpbks,fxntdCeghiry")

,a+1);}

<WtXmlEndTag />

このCプログラムはコンパイル後実行するとというクリスマス・キャロルの十二節が出力される。ソースコード内に韻文全ての文字列が符号化されている。

同じ年に勝者としてはエントリーされていないが、次の例は空白を利用し芸術性のある表現を行う。実行すると任意長の迷路を生成する。

char*M,A,Z,E=40,J[40],T[40];main{for; -- E; J[ E] =T [E ]= E) printf; for ||  , A = 39 ,C -- ) ; Z || printf )M[Z]=Z[A-&&!C& A == T[ A] |6<<27<rand||!C&!Z?J[T[E]=T[A]]=E,J[T[A]=A-Z]=A,"_.":" |"];}

ANSI準拠のCコンパイラでは文字列定数を上書きできないので、"*M"を"M[3]"に変更し、"M="の部分を省略する必要がある。

オスカル・トレド・グティエレスによる次の例は、IOCCCの第19回コンテストにて最優秀作に入選したものである。このコードはターミナルとを完備した8080エミュレータの実装であり、CP/M-80のブートとCP/Mアプリケーションを実行できる。

include
define n=y=%16 p x%16 p o,ap x p o),h(
define s 6[o]
define p z=l[d]|l[d+1]<<8,1<||++8[o]
define Q a
define w 254>||--8[o],l[d]=z,l[1+d]=z>>8
define O )):((
define b >>"\6\0\2\7"[y/2]&1?0:(
define S )?(z-=
define a*-6?&o[f&7]:&l[d])
define C S 5 S 3
define Dx/8!=16+E&198+E*8!=x?
define Bfclose)
define q 
define m x=64&x?*c++:a,
define A=fopen,"rb+")unsigned char o[10],l[78114],*c=l,*k=l

define do[e]+256*o[e-1]
define hs=l>>8&1|128&y|!*64|16&z|2,y^=y>>4,y^=y<<2,y^=~y>>1,s|=y&4+64506; e,V,v,u,x,y,z,Z; maincharU;{

{ { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { ; } } { { { } } } { { ; } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } }

for),"C") ),system,fread,B,"B")),"A")); 118-; -4 S 1 S 1 S 186 S 2 S 2 S 3 S 0,r=*2+y,z=-1 S 2 S 6 S 2 S 182 S 4)?DDDDDDDD?x/64-1?=a O 9 [o]=a,8[o]=a O 237==*c++?))<<7|2[k])<<7,Q=0),y)):0 O y=a,z=a,a=a,a=a,a=y,a=z O c=l+d O y=l[x=d],z=l[++x] ,x[l]=a,l[--x]=a,a=y,a=z O 2-*c?Z||read,1&*c++?Q=Z,Z=0:::-1,s=s&~1|r<0) O++c,write O z=c+2-l,w, c=l+q O p,c=l+z O c=l+q O s^=1 O Q=q[l] O s|=1 O q[l]=Q O Q=~Q O a=l[x=q] ,a=l[++x] O s|=s&16|9<Q%16?Q+=6,16:0,z=s|=1&s|Q>159?Q+=96,1:0,y=Q,h O l[x=q]=a,l[++x]=a O x=Q%2,Q=Q/2+s%2*128,s=s&~1|x O Q=l[d]O x=Q / 128,Q=Q*2+s%2,s=s&~1|x O l[d]=Q
O s=s&~1|1&Q,Q=Q/2|Q<<7 O Q=l[d]O s=~1&s|Q>>7,Q=Q*2|Q>>7 O l[d]=Q O m y ny) O m z=0,y=Q|=x,h O m z=0, y=Q^=x,h O m z=Q*2|2*x,y=Q&=x,h O m Q ny) O m Q ny) O m Q ny) O m Q ny) O z=r-8?d:s|Q<<8,w O p,r-8?o[r+1]=z,r [o]=z>>8: O r[o]--||--o[r-1]O a=z=a+r[o],a=z=a +o[r-1]+z/256,s=~1&s|z>>8 O ++o[r+1]||r[o]++O o[r+1]=*c++,r[o]=*c++O z=c-l,w ,c=y*8+l O x=q,b z=c-l,w,c=l+x) O x=q,b c=l+x) O b p,c=l+z) O a=*c++O r=y ,x=0,ans<<8) O r=y,x=0,ans<<8)))); system; B:0,u)),v)); }<WtXmlEndTag />

次は、の例である。

@P=split//,".URRUU\c8R";@d=split//," rekcah xinU / lreP rehtona tsuJ";sub p{ @p{"r$p","u$p"}=;pipe"r$p","u$p";++$p;+=$f=!fork;map{$P=$P[$f^ord &6];$p{$_}=/ ^$P/ix?$P:close$_}keys%p}p;p;p;p;p;map{$p{$_}=~/^[P.]/&& close$_}%p;wait until$?;map{/^r/&&<$_>}%p;$_=$d[$q];sleep randif/\S/;print

これは"Just another Perl / Unix hacker"という文字列のうち、一塊の文字列を一旦表示し、その後ゆっくり次々と文字列を出力していく。

いくつかのPythonを用いた例は、公式のPythonプログラミングFAQなどにある。

3. 難読化の欠点
---------

せいぜい、難読化は単なる時間稼ぎに過ぎず、プログラムのリバースエンジニアリングを不可能とするものではない。実装によってはパフォーマンスの低下を引き起こすほか、依存性の注入などの目的で、参照する対象の名前を文字列で与えるようなリフレクション・自己反映計算APIを利用している場合などでも、原理的に難読化によって動かなくなるため、難読化が可能な部分が制限される。

また、難読化は必然的に第三者による安全性や正当性の検証を阻害するため、Webブラウザの拡張機能などのプラットフォームではセキュリティの観点から禁止されつつある。

4. 難読化コードを生成するソフトウェア
--------------------

コードの難読化を実行したりサポートするソフトウェアは、//obfuscators//とよばれるプログラムを例に、多く存在する。これらは学術目的による研究用ツールや趣味、専門家によって作成された商用製品やオープンソースソフトウェアもある。また逆に難読化を解除するツールも存在する。

商用の難読化ソリューションはソースコードの難読化やJavaや.NETなどのプラットフォーム独立なバイトコードの変換が大部分を占めるが、中にはコンパイルされたバイナリに直接作用するものも存在する。

5. 関連項目
-------

スパゲティプログラム
逆コンパイル
難解プログラミング言語
クワイン
ポリモルフィックコード
IOCCC
トランスコンパイラ
デジタル著作権管理

6. 脚注
-----

6.1. 注釈
-------

6.2. 出典
-------

7. 外部リンク
--------

[1]
[2]、2009年4月。
[3] - Visual Studioの、.NETコードの難読化を行うビルトインツールのドキュメント。
[4]
[5]
[6]
[7]
[8]
c2:BlackBoxComputation
[9] - コードの難読化はコードの維持管理を困難にする。