難読化コード
難読化コード(Obfuscated code)とは、人間が理解しにくいソースコードやマシンコードを指す用語である。プログラマは、主に、コードの目的を隠蔽するためや(難解さに基づくセキュリティ)、改竄(タンパリング)やリバースエンジニアリングを阻止するため、またはコードを解読するもののためのパズルや娯楽として、故意にコードやロジックを難読化、曖昧化する。obfuscatorsとよばれるプログラムは可読コードを様々な手法を駆使し難読化コードに変換する。コード難読化はハードウェアの曖昧化とは本質的に異なっており、後者は電子回路の配置、構造を機能隠蔽を目的として改変することを意味する。
目次 |
概要 [編集]
他のプログラミング言語に比べ、いくつかの言語はその構造や性質により、難読化が容易なものもある[1][2]。C言語[3][4]、C++[5]、Perl[6]はその一例である。
娯楽としての難読化 [編集]
難読化ソースコードの作成や解読はプログラマにとって、頭の体操(brain teaser)となる。いくつかコンテストもあり、最も創作性の高いものに賞を与えるコンテストもある。例えば、国際邪悪なCコードコンテスト(IOCCC)、Obfuscated Perl Contest、International Obfuscated Ruby Code Contest[7]などである。
難読化のパターンには色々あり、単純なキーワード置換、スペースをうまく利用して芸術的な表現を行う、はたまた、自己生成やデータを高圧縮するものなど様々である。
Perlプログラマの中には署名欄のシグネチャに短く難読化されたPerlプログラムを組み込んでいるものもいる。このような署名にはJAPHs(Just another Perl hacker)などがある。[要出典]
例 [編集]
これは1988年のIOCCCでエントリーされたコードである。作者はイアン・フィリップス(Ian Phillipps)[8]、その後トーマス・ボール(Thomas Ball)により解読されている[9]。
/* LEAST LIKELY TO COMPILE SUCCESSFULLY: Ian Phillipps, Cambridge Consultants Ltd., Cambridge, England */ #include <stdio.h> main(t,_,a) char * a; { return! 0<t? t<3? main(-79,-13,a+ main(-87,1-_, main(-86, 0, a+1 ) +a)): 1, t<_? main(t+1, _, a ) :3, main ( -94, -27+t, a ) &&t == 2 ?_ <13 ? main ( 2, _+1, "%s %d %d\n" ) :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(31[a]): main(-65,_,a+1) : main((*a == '/') + t, _, a + 1 ) : 0<t? main ( 2, 2 , "%s") :*a=='/'|| main(0, main(-61,*a, "!ek;dc i@bK'(q)-[w]*%n+r3#l,{}:\nuwloca-O;m .vpbks,fxntdCeghiry") ,a+1);}
このCプログラムはコンパイル後実行するとクリスマスの十二日というクリスマス・キャロルの十二節が出力される。ソースコード内に韻文全ての文字列が符号化されている。
同じ年に勝者としてはエントリーされていないが、次の例は空白を利用し芸術性のある表現を行う。実行すると任意長の迷路を生成する[10]。
char*M,A,Z,E=40,J[40],T[40];main(C){for(*J=A=scanf(M="%d",&C); -- E; J[ E] =T [E ]= E) printf("._"); for(;(A-=Z=!Z) || (printf("\n|" ) , A = 39 ,C -- ) ; Z || printf (M ))M[Z]=Z[A-(E =A[J-Z])&&!C & A == T[ A] |6<<27<rand()||!C&!Z?J[T[E]=T[A]]=E,J[T[A]=A-Z]=A,"_.":" |"];}
モダンなCコンパイラでは文字列定数(リテラル)を上書きできないので、"*M"を"M[3]"に変更し、"M="の部分を省略する必要がある。
オスカル・トレド・グティエレス(Óscar Toledo Gutiérrez)による次の例は、IOCCCの第19回コンテストにて最優秀作に入選(Best of Show entry)したものである。このコードはターミナルとディスク・コントローラを完備した8080エミュレータの実装であり、CP/M-80のブートとCP/Mアプリケーションを実行できる[11]。
#include <stdio.h> #define n(o,p,e)=y=(z=a(e)%16 p x%16 p o,a(e)p x p o),h( #define s 6[o] #define p z=l[d(9)]|l[d(9)+1]<<8,1<(9[o]+=2)||++8[o] #define Q a(7) #define w 254>(9[o]-=2)||--8[o],l[d(9)]=z,l[1+d(9)]=z>>8 #define O )):(( #define b (y&1?~s:s)>>"\6\0\2\7"[y/2]&1?0:( #define S )?(z-= #define a(f)*((7&f)-6?&o[f&7]:&l[d(5)]) #define C S 5 S 3 #define D(E)x/8!=16+E&198+E*8!=x? #define B(C)fclose((C)) #define q (c+=2,0[c-2]|1[c-2]<<8) #define m x=64&x?*c++:a(x), #define A(F)=fopen((F),"rb+") unsigned char o[10],l[78114],*c=l,*k=l #define d(e)o[e]+256*o[e-1] #define h(l)s=l>>8&1|128&y|!(y&255)*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; main(r,U)char**U;{ { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { ; } } { { { } } } { { ; } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } { { { } } } for(v A((u A((e A((r-2?0:(V A(1[U])),"C") ),system("stty raw -echo min 0"),fread(l,78114,1,e),B(e),"B")),"A")); 118-(x =*c++); (y=x/8%8,z=(x&199)-4 S 1 S 1 S 186 S 2 S 2 S 3 S 0,r=(y>5)*2+y,z=(x& 207)-1 S 2 S 6 S 2 S 182 S 4)?D(0)D(1)D(2)D(3)D(4)D(5)D(6)D(7)(z=x-2 C C C C C C C C+129 S 6 S 4 S 6 S 8 S 8 S 6 S 2 S 2 S 12)?x/64-1?((0 O a(y)=a(x) O 9 [o]=a(5),8[o]=a(4) O 237==*c++?((int (*)())(2-*c++?fwrite:fread))(l+*k+1[k]* 256,128,1,(fseek(y=5[k]-1?u:v,((3[k]|4[k]<<8)<<7|2[k])<<7,Q=0),y)):0 O y=a(5 ),z=a(4),a(5)=a(3),a(4)=a(2),a(3)=y,a(2)=z O c=l+d(5) O y=l[x=d(9)],z=l[++x] ,x[l]=a(4),l[--x]=a(5),a(5)=y,a(4)=z O 2-*c?Z||read(0,&Z,1),1&*c++?Q=Z,Z=0:( Q=!!Z):(c++,Q=r=V?fgetc(V):-1,s=s&~1|r<0) O++c,write(1,&7[o],1) 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(5)=l[x=q] ,a(4)=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(s<<8) O l[x=q]=a(5),l[++x]=a(4) O x=Q%2,Q=Q/2+s%2*128,s=s&~1|x O Q=l[d(3)]O x=Q / 128,Q=Q*2+s%2,s=s&~1|x O l[d(3)]=Q O s=s&~1|1&Q,Q=Q/2|Q<<7 O Q=l[d(1)]O s=~1 &s|Q>>7,Q=Q*2|Q>>7 O l[d(1)]=Q O m y n(0,-,7)y) O m z=0,y=Q|=x,h(y) O m z=0, y=Q^=x,h(y) O m z=Q*2|2*x,y=Q&=x,h(y) O m Q n(s%2,-,7)y) O m Q n(0,-,7)y) O m Q n(s%2,+,7)y) O m Q n(0,+,7)y) O z=r-8?d(r+1):s|Q<<8,w O p,r-8?o[r+1]=z,r [o]=z>>8:(s=~40&z|2,Q=z>>8) O r[o]--||--o[r-1]O a(5)=z=a(5)+r[o],a(4)=z=a(4) +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(y)=*c++O r=y ,x=0,a(r)n(1,-,y)s<<8) O r=y,x=0,a(r)n(1,+,y)s<<8)))); system("stty cooked echo"); B((B((V?B(V):0,u)),v)); }
次は、Just another Perl hackerの例である。
@P=split//,".URRUU\c8R";@d=split//,"\nrekcah xinU / lreP rehtona tsuJ";sub p{ @p{"r$p","u$p"}=(P,P);pipe"r$p","u$p";++$p;($q*=2)+=$f=!fork;map{$P=$P[$f^ord ($p{$_})&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 rand(2)if/\S/;print
これは"Just another Perl / Unix hacker"という文字列のうち、一塊の文字列を一旦表示し、その後ゆっくり次々と文字列を出力していく[12]。
難読化の欠点 [編集]
せいぜい、難読化は単なる時間稼ぎに過ぎず、プログラムのリバースエンジニアリングを不可能とするものではない[14]。また、Javaなどの"Reflection API"の利用では限られている[15]。
難読化コードを生成するソフトウェア [編集]
コードの難読化を実行したりサポートするソフトウェアは多く存在する。これらは学術目的による研究用ツールや趣味、専門家によって作成された商用製品やオープンソースソフトウェアもある。また逆に難読化を解除するツールも存在する。
商用の難読化ソリューションはソースコードの難読化[16][17]やJava[18]や.NET[19]などのプラットフォーム独立なバイトコードの変換が大部分を占めるが、中にはCやC++バイナリ用などネイティブコードに対応するものも存在する[20][21]。
脚注 [編集]
- ^ .NET。 “Obfuscation: Cloaking your Code from Prying Eyes”. www.devx.com. 2008年10月24日時点のオリジナルよりアーカイブ。2011年5月29日閲覧。
- ^ Jeff Atwood (2005年5月15日). “Obfuscating Code”. www.codinghorror.com. 2011年5月29日閲覧。
- ^ Arjan Kenter. “Obfuscation”. www.kenter.demon.nl. 2011年5月29日閲覧。
- ^ “Obfuscated Code”. sites.google.com. 2011年7月8日閲覧。
- ^ “Obfuscated Code - A Simple Introduction”. www.dreamincode.net. 2011年5月29日閲覧。
- ^ “Pe(a)rls in line noise”. www.perlmonks.org. 2011年5月29日閲覧。
- ^ “IORCC”. iorcc.blogspot.com. 2011年5月29日閲覧。
- ^ “International Obfuscated C Code Winners 1988 - Least likely to compile successfully”. IOCCC. 2011年5月29日閲覧。
- ^ Thomas Ball. “Reverse Engineering the Twelve Days of Christmas”. IOCCC. 2011年5月29日閲覧。
- ^ Don Libes, Obfuscated C and Other Mysteries, John Wiley & Sons, 1993, pp 425. ISBN 0-471-57805-3
- ^ Óscar Toledo Gutiérrez. “Intel 8080 emulator. 19th IOCCC. Best of Show.”. nanochess.110mb.com. 2011年9月25日閲覧。
- ^ “Just Another Perl / Unix Hacker”. perl.plover.com. 2011年5月29日閲覧。
- ^ “Is it possible to write obfuscated one-liners in Python?”. docs.python.org. 2011年5月29日閲覧。
- ^ Boaz Barak. “Can We Obfuscate Programs?”. www.math.ias.edu. 2011年5月29日閲覧。
- ^ “Can I always use the Reflection API if the code is going to be obfuscated?”. stackoverflow.com. 2011年5月29日閲覧。
- ^ “Open Directory - Computers: Programming: Languages: JavaScript: Tools: Obfuscators”. dmoz. 2011年5月29日閲覧。
- ^ “Open Directory - Computers: Programming: Languages: PHP: Development Tools: Obfuscation and Encryption”. dmoz. 2011年5月29日閲覧。
- ^ “Open Directory - Computers: Programming: Languages: Java: Development Tools: Obfuscators”. dmoz. 2011年5月29日閲覧。
- ^ “Open Directory - Computers: Programming: Component Frameworks: .NET: Tools: Obfuscators”. dmoz. 2011年5月29日閲覧。
- ^ “Cloakware Application Security”. www.irdeto.com. 2011年5月29日閲覧。
- ^ “Morpher - Compiler Driven Obfuscation”. www.morpher.com. 2011年5月29日閲覧。
参考文献 [編集]
- B. Barak, O. Goldreich, R. Impagliazzo, S. Rudich, A. Sahai, S. Vadhan and K. Yang. "On the (Im)possibility of Obfuscating Programs". 21st Annual International Cryptology Conference, Santa Barbara, California, USA. Springer Verlag LNCS Volume 2139, 2001.
- Mateas, Michael; Nick Montfort. “A Box, Darkly: Obfuscation, Weird Languages, and Code Aesthetics”. Proceedings of the 6th Digital Arts and Culture Conference, IT University of Copenhagen, 1–3 December 2005. pp. 144–153
関連項目 [編集]
外部リンク [編集]
- The International Obfuscated C Code Contest
- Protecting Java Code Via Code Obfuscation, ACM Crossroads, Spring 1998 issue
- Protect Your Java Code - Through Obfuscators And Beyond、2009年4月。
- Dotfuscator in Visual Studio on MSDN resource page — Visual Studio 2008の、.NETコードの難読化を行うビルトインツールのドキュメント。
- Obfuscation tools for .NET, on MSDN — Microsoft Developer Centerサイトにある.NET難読化のためのリソース。
- Can we obfuscate programs?
- Yury Lifshits. Lecture Notes on Program Obfuscation(2005年4月)
- Obfuscate member names in .NET code
- Java obfuscators - Open Directory Project
- 12 daysプログラムの解析。
- 迷路生成プログラムの解析。
- Obfuscated Perl program with explanation
- Making C compiler generate obfuscated code
- Protect php code via code obfuscation
- wiki:BlackBoxComputation
- How To Write Unmaintainable Code - コードの難読化はコードの維持管理を困難にする。