diff --git a/1994/shapiro/Makefile b/1994/shapiro/Makefile index 784d635e5..1fb42498a 100644 --- a/1994/shapiro/Makefile +++ b/1994/shapiro/Makefile @@ -109,7 +109,7 @@ ENTRY= shapiro PROG= ${ENTRY} # OBJ= ${PROG}.o -DATA= shapiro.info +DATA= shapiro.md TARGET= ${PROG} # ALT_OBJ= diff --git a/1994/shapiro/README.md b/1994/shapiro/README.md index 6c59f1535..59d856847 100644 --- a/1994/shapiro/README.md +++ b/1994/shapiro/README.md @@ -1,62 +1,96 @@ # Most Well Rounded Obfuscation: - Andrew T. Shapiro - CSES/CIRES University of Colorado - Campus Box 216 - Boulder, CO 80309-0216 - USA +Andrew T. Shapiro +CSES/CIRES University of Colorado +Campus Box 216 +Boulder, CO 80309-0216 +USA ## To build: make all -## To run +[Cody Boone Ferguson](/winners.html#Cody_Boone_Ferguson) pointed out a potential +bug on systems where `EOF != -1`. The problem is that `getc()` and the related +functions do not return `-1` on EOF or error but rather `EOF` but the program +assumes `-1` (conversely when using the `getopt()` functions one should not +check for EOF but -1 as the functions return -1 not EOF but the standard does +not require `EOF` to be -1, just a negative number. However changing it to check +for `!=EOF` caused a problem due to the clever encoding of the code. See +[bugs.md](/bugs.md) for more details if you wish to try and fix this (Cody is +here and there working on it along with other things and will change this +message when it's fixed assuming it's feasible). + + +## To run: ./shapiro & -## Judges' comments +## Try: - This entry has many different levels of obfuscation, and yet the - source file is self documenting. :-) +```sh +./shapiro +^Z +bg +ps x +^C - From time to time, run ps and look at the new processes. +./shapiro_t1 +``` - See shapiro.info for more information in the internals of this program. - The file shapiro.alt contains a non-obfuscated version of this program. +Notice what you see in the output of `ps`! -## Author's comments - The basic theme (pun) of this program is: - - "This time (everything) is not where it should be." - ~~~~ - My entry (shapiro.c) is mostly comment, formatted in the shape of a - clock. If you strip out the comments and look at the code you will - quickly realize that the comments were the important part and that - the code does very little (see pun above). It writes (to stdout) - another C program (shapiro_t2.c). This is the first level of - obfuscation. - - The second program (shapiro_t2.c if you use the build/make file) - prints a clock in the upper right hand corner of your VTxxx/ANSI - display. - - Most of the surface obfuscation in the second program (shapiro_t2.c) - was an attempt to make it as small as possible. You should be able to - see around this with cb(1) and some more intelligent variable names. - Once you get past this you will realize that the third level of - obfuscation is a six member client/server hierarchy. - (See the second info file for a detailed description of the algorithm.) - - Lint complains about: precedence confusion, K may be used before set, - main() returns random value to invocation environment, value type used - inconsistently, value type declared inconsistently, function argument - ( number ) used inconsistently, function returns value which is always - ignored, function returns value which is sometimes ignored - All of which are harmless (famous last words). - - This may or may not work on non ASCII systems (I could not find one to - test it on.) +## Judges' comments: + +This entry has many different levels of obfuscation, and yet the +source file is self documenting. :-) + +From time to time, run ps and look at the new processes. + +See [shapiro.md](shapiro.md) for more information in the internals of this program. +The file [shapiro.alt.c](shapiro.alt.c) contains a non-obfuscated version of +this program. Note that this alt file is currently missing. See +[bugs.md](/bugs.md) for details. + + +## Author's comments: + +The basic theme (pun) of this program is: + + +> "This time (everything) is not where it should be." +> ~~~~ + + +My entry ([shapiro.c](shapiro.c)) is mostly comment, formatted in the shape of a +clock. If you strip out the comments and look at the code you will +quickly realize that the comments were the important part and that +the code does very little (see pun above). It writes (to stdout) +another C program ([shapiro_t2.c](shapiro_t2.c)). This is the first level of +obfuscation. + +The second program ([shapiro_t2.c](shapiro_t2.c) if you use the Makefile +prints a clock in the upper right hand corner of your VTxxx/ANSI display. + +Most of the surface obfuscation in the second program +([shapiro_t2.c](shapiro_t2.c)) +was an attempt to make it as small as possible. You should be able to +see around this with cb(1) and some more intelligent variable names. +Once you get past this you will realize that the third level of +obfuscation is a six member client/server hierarchy. +(See the [shapiro.md](shapiro.md) file for a detailed description of the +algorithm.) + +Lint complains about: precedence confusion, `K` may be used before set, +main() returns random value to invocation environment, value type used +inconsistently, value type declared inconsistently, function argument +(number) used inconsistently, function returns value which is always +ignored, function returns value which is sometimes ignored. +All of which are harmless (famous last words). + +This may or may not work on non ASCII systems (I could not find one to +test it on.) ## Copyright and CC BY-SA 4.0 License: diff --git a/1994/shapiro/shapiro.info b/1994/shapiro/shapiro.info deleted file mode 100644 index c81070fb7..000000000 --- a/1994/shapiro/shapiro.info +++ /dev/null @@ -1,109 +0,0 @@ -HINTS -~~~~~ -For the first program (prog.c): -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -This is a very simple program: - - 1. #include - 2. main() - 3. { - 4. int K,L,M=0; - 5. FILE *J; - 6. J=fopen(__FILE__,"r"); - 7. for(; - 8. (L=getc(J))!=-1; - 9. L>='J'&&L<='J'+15 && -10. ((M=!M)?K=L:putchar(K-'J'|(L-'J'<<4))) -11. ); -12. exit(0)} - -Lines 1-5 are basic stuff. Line 6 opens the source code for input. Line 7 -is the loop. Line 8 is read in each character in the file (placed in L) -while not EOF(-1). Line 9 is an if expression which filters out all -characters not between J and J + 15. Line 10 uses M to execute two different -paths the first just stores the character in K. The second path combines -the last two characters read in as high and low nibbles of a byte and -outputs them. In effect this is a very poor ASCII to binary decoder. -The last line is necessary so that make will not stop when it executes -this program. - -The second program is encoded into this format and then included (mostly) -as a comment. - -Notice that it is impossible to write this program while leaving 16 -contiguous ASCII characters free for the encoding. Because of this the -second program is mostly included as a comment. However, some of the -characters of the encoded program are also source code for the decoder. - -For the second program (time.c): -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - 1 #include - 2 #include - 3 #define o(j,k,m,n,l) {fprintf(j,a[k],m,n,a[l]);fflush(j);} - 4 char *a[]={"%c+------------+\n","%c+%3d %-8s+\n","seconds","minutes", - 5 "hours","%s\n","\0337\033[%d;65;H%s\0338"}; - 6 int b[]={0,0,17,14,11,0}; - 7 main(v,c) - 8 int v; - 9 char ** c; - 10 { - 11 char d[40],*e,f=6; - 12 time_t g; - 13 FILE *h,*i; - 14 while (--f && !pipe(d) && !fork()); - 15 h=fdopen(6-f<<1,"a"); - 16 i=fdopen((6-f<<1)+1,"r"); - 17 for(;;){ - 18 if(f){ - 19 fgets(d,40,i); - 20 d[strlen(d)-1]='\0'; - 21 if(*d=='0'+f){ sprintf(*c,"%s\n",d+1); o(stdout,6,6-f,d+1,0) } - 22 if(*d>'0'+f) o(h,5,d,0,0) - 23 }else{ - 24 time(&g); - 25 e=asctime(localtime(&g)); - 26 for(v=1;v<6;v++) o(h,v!=1&&v!=6-1,v+'0',atoi(e+b[v]),v) - 27 sleep(1); - 28 }}} - -Lines 1 and 2 have to be. Think of 'o' in line 3 as send message with -j,k and m as where, format and what. Lines 4 and 5 are all the text -strings. Line 6 is the offsets into the time string for the hours, -minutes and seconds. Lines 7-13 are basic stuff. Line 14 is the crux of -the biscuit. 14 forks off 'f'(6) processes each of which is connected to -the ones above and below it with a Unix pipe. It was too difficult to -recover the actual fd numbers for the pipe ends in line 14 so I regenerate -them and fdopen them (for use with the standard C library functions) on lines -15 and 16. Now we have six almost identical processes. Each of which has -two files open h and i (up and down). The only difference in each process -is the value of f (0 <= f < 6). The five processes where f !=0 are the -slave processes and f == 0 is the master. Line 17 says we are going to do -this forever. Lines 18 and 23 differentiate between the master and the -slaves. - -Lines 19 to 22 are the slave code. Here we read in a line of text from -i (down)(line 19) and remove the end of line char (line 20). On line -21 if this message is for us (if the first character is our number, f, -in ASCII) we do two things: -1. We copy the text of the message to *c which is really argv[0]. - If you are on the correct flavor of Unix this will change the output of - the ps(1) command to the string we just put there. -2. We send the message (not including the number) to stdout with - format number 6 ("\0337\033[%d;65;H%s\0338") If you can read VTxxx - (and I read books printed as ANSI/VTxxx codes for fun :-) you know this - is the sequence for: remember where we are, - go somewhere else, - print the message, - and return to where we started. -Line 22 says if this message is not for us (the number is bigger than ours) -send it to h (up). - -Lines 24 to 27 are the master code. Here we get the time and convert it -to an ASCII format (lines 24 and 25). Then on line 26 we build and send -each of the messages for the slaves. The message uses a different control -string for the different slaves. For the first and last slave the message -text is just "%c+------------+\n" (slave number, text, newline.) For the -other slaves it is "%c+%3d %-8s+\n" (slave number, number, units, newline). -The slave number is v+'0'. The number (if used) is atoi(e+b[v]) and the -units are just the string in the slave number position. - diff --git a/1994/shapiro/shapiro.md b/1994/shapiro/shapiro.md new file mode 100644 index 000000000..28b3e4eca --- /dev/null +++ b/1994/shapiro/shapiro.md @@ -0,0 +1,118 @@ +# HINTS + +For the first program (prog.c): + + +This is a very simple program: + +``` + 1. #include + 2. main() + 3. { + 4. int K,L,M=0; + 5. FILE *J; + 6. J=fopen(__FILE__,"r"); + 7. for(; + 8. (L=getc(J))!=EOF; + 9. L>='J'&&L<='J'+15 && +10. ((M=!M)?K=L:putchar(K-'J'|(L-'J'<<4))) +11. ); +12. exit(0);} +``` + +Lines 1-5 are basic stuff. Line 6 opens the source code for input. Line 7 +is the loop. Line 8 is read in each character in the file (placed in `L`) +while not `EOF` (usually `-1`). Line 9 is an if expression which filters out all +characters not between `J` and `J + 15`. Line 10 uses `M` to execute two different +paths the first just stores the character in `K`. The second path combines +the last two characters read in as high and low nibbles of a byte and +outputs them. In effect this is a very poor ASCII to binary decoder. +The last line is necessary so that make will not stop when it executes +this program. + +The second program is encoded into this format and then included (mostly) +as a comment. + +Notice that it is impossible to write this program while leaving 16 +contiguous ASCII characters free for the encoding. Because of this the +second program is mostly included as a comment. However, some of the +characters of the encoded program are also source code for the decoder. + +### For the second program (with time.h): + +``` + 1 #include + 2 #include + 3 #define o(j,k,m,n,l) {fprintf(j,a[k],m,n,a[l]);fflush(j);} + 4 char *a[]={"%c+------------+\n","%c+%3d %-8s+\n","seconds","minutes", + 5 "hours","%s\n","\0337\033[%d;65;H%s\0338"}; + 6 int b[]={0,0,17,14,11,0}; + 7 main(v,c) + 8 int v; + 9 char ** c; +10 { +11 char d[40],*e,f=6; +12 time_t g; +13 FILE *h,*i; +14 while (--f && !pipe(d) && !fork()); +15 h=fdopen(6-f<<1,"a"); +16 i=fdopen((6-f<<1)+1,"r"); +17 for(;;){ +18 if(f){ +19 fgets(d,40,i); +20 d[strlen(d)-1]='\0'; +21 if(*d=='0'+f){ sprintf(*c,"%s\n",d+1); o(stdout,6,6-f,d+1,0) } +22 if(*d>'0'+f) o(h,5,d,0,0) +23 }else{ +24 time(&g); +25 e=asctime(localtime(&g)); +26 for(v=1;v<6;v++) o(h,v!=1&&v!=6-1,v+'0',atoi(e+b[v]),v) +27 sleep(1); +28 }}} + +``` + +Lines 1 and 2 have to be. Think of `o` in line 3 as send message with +`j`,`k` and `m` as where, format and what. Lines 4 and 5 are all the text +strings. Line 6 is the offsets into the time string for the hours, +minutes and seconds. Lines 7-13 are basic stuff. Line 14 is the crux of +the biscuit. 14 forks off `f` (6) processes each of which is connected to +the ones above and below it with a Unix pipe. It was too difficult to +recover the actual FD numbers for the pipe ends in line 14 so I regenerate +them and `fdopen()` them (for use with the standard C library functions) on lines +15 and 16. Now we have six almost identical processes. Each of which has +two files open `h` and `i` (up and down). The only difference in each process +is the value of `f` (`0 <= f < 6`). The five processes where `f !=0` are the +slave processes and `f == 0` is the master. Line 17 says we are going to do +this forever. Lines 18 and 23 differentiate between the master and the +slaves. + +Lines 19 to 22 are the slave code. Here we read in a line of text from +`i` (down)(line 19) and remove the end of line char (line 20). On line +21 if this message is for us (if the first character is our number, `f`, +in ASCII) we do two things: + +1. We copy the text of the message to `*c` which is really argv[0]. If you are +on the correct flavor of Unix this will change the output of the `ps(1)` command +to the string we just put there. +2. We send the message (not including the number) to stdout with format number 6 +(`"\0337\033[%d;65;H%s\0338"`) If you can read VTxxx (and I read books printed +as ANSI/VTxxx codes for fun :-) ) you know this is the sequence for: + + remember where we are, + go somewhere else, + print the message, + and return to where we started. + +Line 22 says if this message is not for us (the number is bigger than ours) +send it to `h` (up). + +Lines 24 to 27 are the master code. Here we get the time and convert it +to an ASCII format (lines 24 and 25). Then on line 26 we build and send +each of the messages for the slaves. The message uses a different control +string for the different slaves. For the first and last slave the message +text is just `"%c+------------+\n"` (slave number, text, newline.) For the +other slaves it is `"%c+%3d %-8s+\n"` (slave number, number, units, newline). +The slave number is `v+'0'`. The number (if used) is `atoi(e+b[v])` and the +units are just the string in the slave number position. +