In the early days of computer science coding was viewed as an art. In the modern world of software engineering we may have lost the art to make way for rules and best practices. The International Obfuscated C Code Contest offers a chance for the coder to think beyond the rules of software engineering and unleash their creative side. We'll explore some of the more interesting entries in the past, take a closer look at some exotic C syntax, and finish up by exploring Bruce Holloway's 1986 entry.
From the Un-Distinguished Lecture Series (http://ws.cs.ubc.ca/~udls/). The talk was given Feb. 2, 2007
1. C Code and the Art of
Obfuscation
Lloyd Markle
nd
February 2 , 2007
2. A Little background
● What's C?
– C is a programming language
– Designed at AT&T Bell Labs
– Available on every platform
– The most popular language of all time?
– Kerningham & Ritchie published the first good
reference (fondly referred to as K&R C)
3. A Little C
● Some C Syntax
int x, y;
x = 5;
y = x;
printf( “x = %d, y = %dn”, x, y );
● Some more...
if( x > y ) {
printf( “x is bigger than yn” );
}
else {
printf( “x is not bigger than yn” );
}
int i;
for( i = 0; i < 10; i++ ) {
printf( “%dn”, i );
}
4. A Little C Program
● This...
#include <stdio.h>
int main() {
printf( “hello world!n” );
}
● Outputs this...
hello world!
5. More Syntax
Syntax Result
++i i is increased by 1
i++ i is evaluated, then increased
i += 1 i=i+1
i -= 1 i=i–1
i *= 1 i=i*1
i<j 1 if i is greater than j
i>j 1 if i is greater than j
!( i < j ) 0 if i is greater than j
7. Exotic Syntax 101
This is easy...
int x, y;
x = 5;
y = ( x = 5 );
But what about this?
int x;
x = ( 5 == 5 );
8. Exotic Syntax 101
Consider these...
int y;
y = 5 == 4;
int x, y;
x = 5;
y = ( x = 5 )++;
int x, y, i;
for( i = 0, x = 0, y = 5; x++ < --y; i++ )
printf( “%dn”, i );
printf( “%dn”, i );
18. Obfuscation
● What is obfuscation?
– “Obfuscate: tr.v. -cated, -cating, -cates. 1. a) To
render obscure. b) To darken. 2. To confuse: his
emotions obfuscated his judgment.” -- ioccc.org
– “Obfuscation refers to the concept of concealing the
meaning of communication by making it more
confusing and harder to interpret” -- wikipedia.org
19. Why Obfuscate?
● Mainly to prevent reverse engineering
– Java or C# programs are easy to decompile
– Obfuscated code is difficult to read
– Should have no value to unauthorized users
● Fun!
– The International Obfuscated C Code Contest
(IOCCC) http://www.ioccc.org/
– After all, coding is an art!
20. How to Obfuscate
● Take an easy task, make it hard!
● Meaningless variable/function names
● For the IOCCC you can try:
– Interesting code format
– Complex C syntax
● In general be creative!
25. Why We Love C
● Consider the following...
#include <stdio.h>
int main() {
printf( “hello world!n” );
}
26. Why We Love C
● This is equivalent...
#include <stdio.h>
int main( int argc, char **argv ) {
printf( “hello world!n” );
}
27. Why We Love C
● And so is this...
#include <stdio.h>
int main(int argc,char**argv){printf(“hello world!n”);}
28. Why We Love C
● And so is this...
#include <stdio.h>
#define WHY_I_LOVE_C “hello world!”
int main( int argc, char **argv ) {
char *x = WHY_I_LOVE_C;
int i;
for( i = 0; i < 13; i++ )
printf( “%c”, x[i] );
printf( “n” );
}
29. Why We Love C
● And so is this...
/* Program by Bruce Holloway, Digital Research */
#include quot;stdio.hquot;
#define e 3
#define g (e/e)
#define h ((g+e)/2)
#define f (e-g-h)
#define j (e*e-g)
#define k (j-h)
#define l(x) tab2[x]/h
#define m(n,a) ((n&(a))==(a))
long tab1[]={ 989L,5L,26L,0L,88319L,123L,0L,9367L };
int tab2[]={ 4,6,10,14,22,26,34,38,46,58,62,74,82,86 };
main(m1,s) char *s; {
int a,b,c,d,o[k],n=(int)s;
if(m1==1){ char b[2*j+f-g]; main(l(h+e)+h+e,b); printf(b); }
else switch(m1-=h){
case f:
a=(b=(c=(d=g)<<g)<<g)<<g;
return(m(n,a|c)|m(n,b)|m(n,a|d)|m(n,c|d));
case h:
for(a=f;a<j;++a)if(tab1[a]&&!(tab1[a]%((long)l(n))))return(a);
case g:
if(n<h)return(g);
if(n<j){n-=g;c='D';o[f]=h;o[g]=f;}
else{c='r'-'b';n-=j-g;o[f]=o[g]=g;}
if((b=n)>=e)for(b=g<<g;b<n;++b)o[b]=o[b-h]+o[b-g]+c;
return(o[b-g]%n+k-h);
default:
if(m1-=e) main(m1-g+e+h,s+g); else *(s+g)=f;
for(*s=a=f;a<e;) *s=(*s<<e)|main(h+a++,(char *)m1);
}
}
30. Why We Love C
● And so is this... but how?
/* Program by Bruce Holloway, Digital Research */
#include quot;stdio.hquot;
#define e 3
#define g (e/e)
#define h ((g+e)/2)
#define f (e-g-h)
#define j (e*e-g)
#define k (j-h)
#define l(x) tab2[x]/h
#define m(n,a) ((n&(a))==(a))
long tab1[]={ 989L,5L,26L,0L,88319L,123L,0L,9367L };
int tab2[]={ 4,6,10,14,22,26,34,38,46,58,62,74,82,86 };
main(m1,s) char *s; {
int a,b,c,d,o[k],n=(int)s;
if(m1==1){ char b[2*j+f-g]; main(l(h+e)+h+e,b); printf(b); }
else switch(m1-=h){
case f:
a=(b=(c=(d=g)<<g)<<g)<<g;
return(m(n,a|c)|m(n,b)|m(n,a|d)|m(n,c|d));
case h:
for(a=f;a<j;++a)if(tab1[a]&&!(tab1[a]%((long)l(n))))return(a);
case g:
if(n<h)return(g);
if(n<j){n-=g;c='D';o[f]=h;o[g]=f;}
else{c='r'-'b';n-=j-g;o[f]=o[g]=g;}
if((b=n)>=e)for(b=g<<g;b<n;++b)o[b]=o[b-h]+o[b-g]+c;
return(o[b-g]%n+k-h);
default:
if(m1-=e) main(m1-g+e+h,s+g); else *(s+g)=f;
for(*s=a=f;a<e;) *s=(*s<<e)|main(h+a++,(char *)m1);
}
}
31. IOCCC Examples (holloway 1986)
● Let's make it a little easier...
/* Program by Bruce Holloway, Digital Research */
#include quot;stdio.hquot;
#define e 3
#define g (e/e)
#define h ((g+e)/2)
#define f (e-g-h)
#define j (e*e-g)
#define k (j-h)
#define l(x) tab2[x]/h
#define m(n,a) ((n&(a))==(a))
long tab1[]={ 989L,5L,26L,0L,88319L,123L,0L,9367L };
int tab2[]={ 4,6,10,14,22,26,34,38,46,58,62,74,82,86 };
main(m1,s) char *s; {
int a,b,c,d,o[k],n=(int)s;
if(m1==1){ char b[2*j+f-g]; main(l(h+e)+h+e,b); printf(b); }
else switch(m1-=h){
case f:
a=(b=(c=(d=g)<<g)<<g)<<g;
return(m(n,a|c)|m(n,b)|m(n,a|d)|m(n,c|d));
case h:
for(a=f;a<j;++a)if(tab1[a]&&!(tab1[a]%((long)l(n))))return(a);
case g:
if(n<h)return(g);
if(n<j){n-=g;c='D';o[f]=h;o[g]=f;}
else{c='r'-'b';n-=j-g;o[f]=o[g]=g;}
if((b=n)>=e)for(b=g<<g;b<n;++b)o[b]=o[b-h]+o[b-g]+c;
return(o[b-g]%n+k-h);
default:
if(m1-=e) main(m1-g+e+h,s+g); else *(s+g)=f;
for(*s=a=f;a<e;) *s=(*s<<e)|main(h+a++,(char *)m1);
}
}
32. IOCCC Examples (holloway 1986)
/* Program by Bruce Holloway, Digital Research */
#include quot;stdio.hquot;
#define e 3
#define g (e/e)
#define h ((g+e)/2)
#define f (e-g-h)
#define j (e*e-g)
#define k (j-h)
#define l(x) tab2[x]/h
#define m(n,a) ((n&(a))==(a))
33. IOCCC Examples (holloway 1986)
/* Program by Bruce Holloway, Digital Research */
#include quot;stdio.hquot;
#define e 3
#define g 1
#define h 2
#define f 0
#define j 8
#define k 6
#define l(x) tab2[x]/h
#define m(n,a) ((n&(a))==(a))
34. IOCCC Examples (holloway 1986)
/* Program by Bruce Holloway, Digital Research */
#include quot;stdio.hquot;
#define l( x ) tab2[ x ] / 2
#define m( n, a ) ( ( n & (a) ) == (a) )
long tab1[] = { 989L, 5L, 26L, 0L, 88319L, 123L, 0L, 9367L };
int tab2[] = { 4, 6, 10, 14, 22, 26, 34, 38, 46, 58, 62, 74, 82, 86 };
main( m1, s ) char *s; {
int a, b, c, d, o[6], n = (int)s;
if( m1 == 1 ) { char b[ 15 ]; main( l( 5 ) + 5, b ); printf( b );}
else {
switch ( m1 -= 2 ) {
case 0:
a = ( b = ( c = ( d = 1 ) << 1 ) << 1 ) << 1;
return ( m( n, a | c ) | m( n, b ) | m( n, a | d ) | m( n, c | d ) );
case 2:
for( a = 0; a < 8; ++a )
if( tab1[ a ] && !( tab1[ a ] % ( (long)l( n ) ) ) ) return ( a );
case 1:
if( n < 2 ) return ( 1 );
if( n < 8 ) { n -= 1; c = 'D'; o[ 0 ] = 2; o[ 1 ] = 0; }
else { c = 'r' - 'b'; n -= 7; o[ 0 ] = o[ 1 ] = 1; }
if( ( b = n ) >= 3 )
for( b = 1 << 1; b < n; ++b ) o[ b ] = o[ b - 2 ] + o[ b - 1 ] + c;
return ( o[ b - 1] % n + 4 );
default:
if( m1 -= 3 ) main( m1 + 4, s + 1 );
else *( s + 1 ) = 0;
for( *s = a = 0; a < 3; ) *s = ( *s << 3 ) | main( 2 + a++, (char *)m1 );
}
}
}
35. IOCCC Examples (holloway 1986)
/* Program by Bruce Holloway, Digital Research */
#include quot;stdio.hquot;
#define l( x ) tab2[ x ] / 2
#define m( n, a ) ( ( n & (a) ) == (a) )
long tab1[] = { 989L, 5L, 26L, 0L, 88319L, 123L, 0L, 9367L };
int tab2[] = { 4, 6, 10, 14, 22, 26, 34, 38, 46, 58, 62, 74, 82, 86 };
main( m1, s ) char *s; {
int a, b, c, d, o[6], n = (int)s;
if( m1 == 1 ) { char b[ 15 ]; main( l( 5 ) + 5, b ); printf( b );}
else {
switch ( m1 -= 2 ) {
case 0:
a = ( b = ( c = ( d = 1 ) << 1 ) << 1 ) << 1;
return ( m( n, a | c ) | m( n, b ) | m( n, a | d ) | m( n, c | d ) );
case 2:
for( a = 0; a < 8; ++a )
if( tab1[ a ] && !( tab1[ a ] % ( (long)l( n ) ) ) ) return ( a );
case 1:
if( n < 2 ) return ( 1 );
if( n < 8 ) { n -= 1; c = 'D'; o[ 0 ] = 2; o[ 1 ] = 0; }
else { c = 'r' - 'b'; n -= 7; o[ 0 ] = o[ 1 ] = 1; }
if( ( b = n ) >= 3 )
for( b = 1 << 1; b < n; ++b ) o[ b ] = o[ b - 2 ] + o[ b - 1 ] + c;
return ( o[ b - 1] % n + 4 );
default:
if( m1 -= 3 ) main( m1 + 4, s + 1 );
else *( s + 1 ) = 0;
for( *s = a = 0; a < 3; ) *s = ( *s << 3 ) | main( 2 + a++, (char *)m1 );
}
}
}
36. IOCCC Examples (holloway 1986)
/* Program by Bruce Holloway, Digital Research */
#include quot;stdio.hquot;
#define l( x ) tab2[ x ] / 2
#define m( n, a ) ( ( n & (a) ) == (a) )
long tab1[] = { 989L, 5L, 26L, 0L, 88319L, ... };
int tab2[] = { 4, 6, 10, 14, 22, 26, 34, 38, ... };
main( m1, s ) char *s; {
int a, b, c, d, o[6], n = (int)s;
if( m1 == 1 ) {
char b[ 15 ];
main( l( 5 ) + 5, b );
printf( b );
}
else {
...
}
}
37. IOCCC Examples (holloway 1986)
/* Program by Bruce Holloway, Digital Research */
#include quot;stdio.hquot;
#define l( x ) tab2[ x ] / 2
#define m( n, a ) ( ( n & (a) ) == (a) )
long tab1[] = { 989L, 5L, 26L, 0L, 88319L, 123L, 0L, 9367L };
int tab2[] = { 4, 6, 10, 14, 22, 26, 34, 38, 46, 58, 62, 74, 82, 86 };
main( m1, s ) char *s; {
int a, b, c, d, o[6], n = (int)s;
if( m1 == 1 ) { char b[ 15 ]; main( l( 5 ) + 5, b ); printf( b );}
else {
switch ( m1 -= 2 ) {
case 0:
a = ( b = ( c = ( d = 1 ) << 1 ) << 1 ) << 1;
return ( m( n, a | c ) | m( n, b ) | m( n, a | d ) | m( n, c | d ) );
case 2:
for( a = 0; a < 8; ++a )
if( tab1[ a ] && !( tab1[ a ] % ( (long)l( n ) ) ) ) return ( a );
case 1:
if( n < 2 ) return ( 1 );
if( n < 8 ) { n -= 1; c = 'D'; o[ 0 ] = 2; o[ 1 ] = 0; }
else { c = 'r' - 'b'; n -= 7; o[ 0 ] = o[ 1 ] = 1; }
if( ( b = n ) >= 3 )
for( b = 1 << 1; b < n; ++b ) o[ b ] = o[ b - 2 ] + o[ b - 1 ] + c;
return ( o[ b - 1] % n + 4 );
default:
if( m1 -= 3 ) main( m1 + 4, s + 1 );
else *( s + 1 ) = 0;
for( *s = a = 0; a < 3; ) *s = ( *s << 3 ) | main( 2 + a++, (char *)m1 );
}
}
}
38. IOCCC Examples (holloway 1986)
switch ( m1 -= 2 ) {
case 0:
a = ( b = ( c = ( d = 1 ) << 1 ) << 1 ) << 1;
return ( m(n,a|c) | m(n,b) | m(n,a|d) | m(n,c|d) );
case 2:
for( a = 0; a < 8; ++a )
if( tab1[ a ] && !( tab1[ a ] % ( (long)l( n ) ) ) )
return ( a );
case 1:
if( n < 2 ) return ( 1 );
if( n < 8 ) { n -= 1; c = 'D'; o[ 0 ] = 2; o[ 1 ] = 0; }
else { c = 'r' - 'b'; n -= 7; o[ 0 ] = o[ 1 ] = 1; }
if( ( b = n ) >= 3 )
for( b = 1 << 1; b < n; ++b )
o[ b ] = o[ b - 2 ] + o[ b - 1 ] + c;
return ( o[ b - 1] % n + 4 );
default:
if( m1 -= 3 ) main( m1 + 4, s + 1 );
else *( s + 1 ) = 0;
for( *s = a = 0; a < 3; )
*s = ( *s << 3 ) | main( 2 + a++, (char *)m1 );
}
39. IOCCC Examples (holloway 1986)
default:
if( m1 -= 3 )
main( m1 + 4, s + 1 );
else
*( s + 1 ) = 0;
for( *s = a = 0; a < 3; )
*s = ( *s << 3 ) | main( 2 + a++, (char *)m1 );
● Build string recursively
● Each character built in three stages
41. String Building
● Need to generate “hello world!”
● But we also need a rn at the end.
● So we need “hello world!rn”
42. String Building
● Need to generate “hello world!”
● But we also need a rn at the end.
● So we need “hello world!rn”
● But we're doing this recursively...
default:
if( m1 -= 3 )
main( m1 + 4, s + 1 );
else
*( s + 1 ) = 0;
for( *s = a = 0; a < 3; )
*s = ( *s << 3 ) | main( 2 + a++, (char *)m1 );
43. String Building
● Need to generate “hello world!”
● But we also need a rn at the end.
● So we need “hello world!rn”
● But we're doing this recursively...
default:
if( m1 -= 3 )
main( m1 + 4, s + 1 );
else
*( s + 1 ) = 0;
for( *s = a = 0; a < 3; )
*s = ( *s << 3 ) | main( 2 + a++, (char *)m1 );
● We need “nr!dlrow olleh”
44. String Building
n ASCII int bin
0 n 10 0001010
1 r 13 0001101
2 ! 33 0100001
3 d 100 1100100
4 l 108 1101100
5 r 114 1110010
6 o 111 1101111
7 w 119 1110111
8 32 0100000
9 o 111 1101111
10 l 108 1101100
11 l 108 1101100
12 e 101 1100101
13 h 104 1101000
58. Case 2
#define l(x) tab2[x] / 2
long tab1[]={ 989L, 5L, 26L, 0L, 88319L, 123L, 0L, 9367L };
int tab2[]={ 4, 6, 10, 14, 22, 26, 34, 38, 46, 58, 62, 74, 82, 86 };
● Make a new tab2...
int ltab2[]={ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43 };
59. Case 2
#define l(x) tab2[x] / 2
long tab1[]={ 989L, 5L, 26L, 0L, 88319L, 123L, 0L, 9367L };
int tab2[]={ 4, 6, 10, 14, 22, 26, 34, 38, 46, 58, 62, 74, 82, 86 };
int ltab2[]={ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43 };
case 2:
for( a = 0; a < 8; ++a )
if( tab1[a] && !( tab1[a] % ((long)ltab2[n])) )
return ( a );
60. Case 2
long tab1[]={ 989L, 5L, 26L, 0L, 88319L, 123L, 0L, 9367L };
● And tab1?
● Anything to do here?
61. Case 2
long tab1[]={ 989L, 5L, 26L, 0L, 88319L, 123L, 0L, 9367L };
● And tab1?
● Anything to do here?
– Notice:
● 23*43 = 989
● 2*13 = 26
● 7*11*31*37 = 88319
● 3*41 = 123
● 17*19*29 = 9367
62. Case 2
long tab1[]={ 23*43, 5, 2*13, 0, 7*11*31*37, 3*41, 0, 17*19*29 };
int ltab2[]={ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43 };
case 2:
for( a = 0; a < 8; ++a )
if( tab1[a] && !( tab1[a] % ((long)ltab2[n])) )
return ( a );
63. Case 2
long tab1[]={ 23*43, 5, 2*13, 0, 7*11*31*37, 3*41, 0, 17*19*29 };
int ltab2[]={ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43 };
case 2:
for( a = 0; a < 8; ++a )
if( tab1[a] && !( tab1[a] % ((long)ltab2[n])) )
return ( a );
● Careful inspection and we see we get the
result:
n 0 1 2 3 4 5 6 7 8 9 10 11 12 13
Case 2 010 101 001 100 100 010 111 111 000 111 100 100 101 000
Case 2 2 5 1 4 4 2 7 7 0 7 4 4 5 0
64. IOCCC Examples (holloway 1986)
switch ( m1 -= 2 ) {
case 0:
a = ( b = ( c = ( d = 1 ) << 1 ) << 1 ) << 1;
return ( m(n,a|c) | m(n,b) | m(n,a|d) | m(n,c|d) );
case 2:
for( a = 0; a < 8; ++a )
if( tab1[ a ] && !( tab1[ a ] % ( (long)l( n ) ) ) )
return ( a );
case 1:
if( n < 2 ) return ( 1 );
if( n < 8 ) { n -= 1; c = 'D'; o[ 0 ] = 2; o[ 1 ] = 0; }
else { c = 'r' - 'b'; n -= 7; o[ 0 ] = o[ 1 ] = 1; }
if( ( b = n ) >= 3 )
for( b = 1 << 1; b < n; ++b )
o[ b ] = o[ b - 2 ] + o[ b - 1 ] + c;
return ( o[ b - 1] % n + 4 );
default:
if( m1 -= 3 ) main( m1 + 4, s + 1 );
else *( s + 1 ) = 0;
for( *s = a = 0; a < 3; )
*s = ( *s << 3 ) | main( 2 + a++, (char *)m1 );
}
65. Case 1
● Is too hard...
case 1: n Case 1 Case 1
if( n < 2 ) return ( 1 ); 0 001 1
if( n < 8 ) { 1 001 1
n -= 1; 2 100 4
c = 'D'; 3 100 4
o[ 0 ] = 2;
o[ 1 ] = 0; 4 101 5
} 5 110 6
else { 6 101 5
c = 'r' - 'b';
n -= 7; 7 110 6
o[ 0 ] = o[ 1 ] = 1; 8 100 4
} 9 101 5
if( ( b = n ) >= 3 ) 10 101 5
for( b = 1 << 1; b < n; ++b ) 11 101 5
o[ b ] = o[ b - 2 ] + o[ b - 1 ] + c; 12 100 4
return ( o[ b - 1] % n + 4 );
13 101 5
66. Case 1
● Is too hard...
case 1: n Case 1 Case 1
if( n < 2 ) return ( 1 ); 0 001 1
if( n < 8 ) { 1 001 1
n -= 1; 2 100 4
c = 'D'; 3 100 4
o[ 0 ] = 2;
o[ 1 ] = 0; 4 101 5
} 5 110 6
else { 6 101 5
c = 'r' - 'b';
n -= 7; 7 110 6
o[ 0 ] = o[ 1 ] = 1; 8 100 4
} 9 101 5
if( ( b = n ) >= 3 ) 10 101 5
for( b = 1 << 1; b < n; ++b ) 11 101 5
o[ b ] = o[ b - 2 ] + o[ b - 1 ] + c; 12 100 4
return ( o[ b - 1] % n + 4 );
13 101 5
67. A Masterpiece: One Last Look
/* Program by Bruce Holloway, Digital Research */
#include quot;stdio.hquot;
#define e 3
#define g (e/e)
#define h ((g+e)/2)
#define f (e-g-h)
#define j (e*e-g)
#define k (j-h)
#define l(x) tab2[x]/h
#define m(n,a) ((n&(a))==(a))
long tab1[]={ 989L,5L,26L,0L,88319L,123L,0L,9367L };
int tab2[]={ 4,6,10,14,22,26,34,38,46,58,62,74,82,86 };
main(m1,s) char *s; {
int a,b,c,d,o[k],n=(int)s;
if(m1==1){ char b[2*j+f-g]; main(l(h+e)+h+e,b); printf(b); }
else switch(m1-=h){
case f:
a=(b=(c=(d=g)<<g)<<g)<<g;
return(m(n,a|c)|m(n,b)|m(n,a|d)|m(n,c|d));
case h:
for(a=f;a<j;++a)if(tab1[a]&&!(tab1[a]%((long)l(n))))return(a);
case g:
if(n<h)return(g);
if(n<j){n-=g;c='D';o[f]=h;o[g]=f;}
else{c='r'-'b';n-=j-g;o[f]=o[g]=g;}
if((b=n)>=e)for(b=g<<g;b<n;++b)o[b]=o[b-h]+o[b-g]+c;
return(o[b-g]%n+k-h);
default:
if(m1-=e) main(m1-g+e+h,s+g); else *(s+g)=f;
for(*s=a=f;a<e;) *s=(*s<<e)|main(h+a++,(char *)m1);
}
}
68.
69. C Code and the Art of Obfuscation
● It should remain an art
● Do not use in practice
● Please try it at home!
● IOCCC submission deadline:
February 28th, 2007
Details at: www.ioccc.org
70. Credits and Acknowledgements
● Art Contributions on slides 17, 20, 24, 49-52,
53, 55-63, 68, and 69 thanks to Asbjorn Lonvig,
(used without permission)
● Special thanks to Bruce Holloway and the
IOCCC
71. Credits and Acknowledgements
● Image on slides 39-47 from www.websitehelper.com/ (used without
permission)
● Image on slide 1 from www.indeutsch.com (used without permission)
● Image on slide 2 from www.onlineprintworks.com (used without permission)
● Image on slide 3 from www.yourhome123.com (used without permission)
● Image on slide 4 from www.purrfection.com (used without permission)
● Image on slide 6 from www.spectris.com (used without permission)
● Image on slides 7-16 from www.germes-online.com (used without
permission)
● Image on slide 18 from www.smartboxat.com (used without permission)