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. + diff --git a/bugs.md b/bugs.md index ad3506e86..2a98c56c7 100644 --- a/bugs.md +++ b/bugs.md @@ -522,13 +522,13 @@ If you join the lines you end up with: If you look at column 25 which is the end of the word 'mh111' and you go down to the next row you'll see a 0 and if you go one row down another 0. This is the -buffer size, 100, for `u`. The column to the left is the same for the `t` +buffer size, 100, for `u`! The column to the left is the same for the `t` variable. Thus it seems that in order to get the generated output correct one needs to provide the correct input in comments or possibly by rearranging some of the -code (this was required to make the generated code compile at all when changing -the buffer size). +code (this was actually required to make the generated code compile at all when +changing the buffer size, see below). ### Important points: @@ -550,6 +550,103 @@ can cause a compilation error! Make sure that the output of: can be compiled and the output of that new program when fed itself can also be compiled! +## [1994/shapiro](1994/shapiro/shapiro.c) ([README.md](1994/shapiro/README.md)) +## STATUS: missing file - please provide it + +[Cody Boone Ferguson](/winners.html#Cody_Boone_Ferguson) noted that the +README.md file refers to an alternative version of the code that is not +obfuscated but it is missing from the entry directory and the archive. Do you +have this file? + +We would be grateful if you could provide it to us. If you can provide this +file you might consider removing this entry from this file as well but if not +we'll take care of it. + +### Important reminder to fix the `-1` value check for `getc()`: + +Cody wants to remind you that he fixed the code to not use `-1` for the return +value of `getc()`; this is important because `EOF` is **NOT** guaranteed to be +`-1` but rather any negative value. On systems where `EOF != -1` the program +would enter an infinite loop until the program crashed, by chance reads a `-1` +or was killed. See the README.md for more details. If you don't feel comfortable +changing it to `EOF` Cody will happily do it for you but otherwise go right +ahead. We'll credit you in the README.md file regardless. + + +## STATUS: doesn't work with some platforms - please help us fix + +[Cody Boone Ferguson](/winners.html#Cody_Boone_Ferguson) pointed out a potential +bug in [1994/shapiro](1994/shapiro/shapiro.c) on systems where `EOF != -1` as +the code assumes that `EOF` will always be `-1`. As the standard says (see `7.21 +Input/output` subsection 1) it only has to be a negative int, not +necessarily `-1`. The problem is that the source file's last character is a +newline (10 in decimal) and so on systems where `EOF != -1` the `getc()` +functions would not terminate the loop (but see below). This would cause a +breakage in the entry. + +However due to clever encoding of the code it is not a simple matter of applying +the following patch: + +```diff +diff --git i/1994/shapiro/shapiro.c w/1994/shapiro/shapiro.c +index d2da120c7bc4e251b7624859e48f47f188d398e9..e054a1ceb067b82bf37c07e5ce75e82be7a03b85 100644 +--- i/1994/shapiro/shapiro.c ++++ w/1994/shapiro/shapiro.c +@@ -9,11 +9,11 @@ + /*PPPPVPOQMQRPR*/=0;FILE/*TPSLUMWQT*/ * + J/*MPRPKPLQ*/; J= fopen(__FILE__ + /*T*/,"r");for (;( L/*KPUOWOWMUQ + LLOLMPULWLWL WLW LWLWLWLWLWLW + LWLWLULVOXP LLV LLLOLMPULOL +- MMNP*/=getc (J) )!= -1;L/* ++ MMNP*/=getc (J) )!=EOF;L/* + OLWLRMMQUL VOX PLLVLLLMQO + PMPYPXPNP MQL LVLLLWPSP + XPOQNQOPMQ LLV LTj*/>='J' + &&L/*LRPYP OQL QMQLLVLLL + OLMQVOXPL LVL LLVOe*/<= + +``` + +as doing so will cause `warning: illegal character encoding in string +literal` and the error `error: source file is not valid UTF-8`. Looking at the +file you'll see on line 4 of `shapiro_t2.c`: + + +``` +char *a[]={"%c+------------+\n","%c+%3dPRÒ<82>3·Âå&Â"2W6öæF6'Â"Ò<96>æVGW6'¢ <82>öV'7'Â"R2Çå&Â"Â^E33sÃ^E33³UB¶cS³<83>T2Ç^E33<83>#Ò·£<90>æF^G"¶ÕÕ³^GÃ^BÃ^RsÃ^RCÃ^R^SÃ^BÓ·£Ð^V<96>æ<86>bÇ2<96><92>æF^Gb·3<86>^V&^G¢¢^B2¶£°7<86>^V&^GB¶E^CÓÅ¢RÆbÖc³£@<97>ÖVöE^Gr¶£`<94>ÄT^D¢<82>Æ¢<92>¶£p<87><96>ÆV^F<82>ÒÒb^Fbb^B^R^B<97>^FW<86>B<96>^Bbb^B^Rbö&·<86><92><92>²£<80>ÖcFö^FWæ<86>bÓbÆÃ^SÃ"^R&<92>²<93>ÖcFö^FWæ<86><82>bÓbÆÃ^S<93>²^RÃ""'<92>²£`ö&<87>²³<93>²<97>f<86>b<96>²gvVF7<87>BÆB^CÃ<92><96>²C¶5G'ÇVæ<86>B<96>Ò^RÓÕsÂ^Es²£<90>f<86>¢BÖÓs^Bs²b<96>²7^G'<97>æFg<86>¢2Æ"R2Çå&ÂB¶^R<93>²ó<86>2GGöVGÇbÃbÓbÆB¶^RÃ^B<93>Ò§<90>f<86>¢Bæs^Bs²b<96>ò<86><82>ÆRÃBÆ^BÃ^B<93>ÒWÆ6W¶G<97>ÖV<86>br<96>²SÖ^S67F<97>ÖV<86>Âö6^VÆF<97>ÖV<86>br<96><92>²£`ö&<87>b×^S³cÇc³c·²<92>ò<86><82>Æb^WÒ^Scbb^WÒcÓ^RÃb·r^BsÂ^RF÷<96><86>R¶"¶e×<95>Âb<97>2ÇVV^F<87>^R<93>²Ó××§  +``` + +when it should be something entirely different. For what it should be try +running `make clobber all ; cat shapiro_t2.c` or else look at the second program +in the spoilers file [shapiro.md](1994/shapiro/shapiro.md]). + +Cody is working will be working on this here and there but if you have a fix or +any suggestions you're welcome to provide it or them. + +Cody points you to the README.md file for what should happen when the entry is +successfully compiled (including no compilation errors when running `make +clobber all`). + +Another problem is that in the [shapiro.md](shapiro.md) file the first program +does not work. + +### Tips on this entry: + +Cody provides some tips on this entry: + +First: the comments are the important part of this entry. Remove them and the +program breaks. + +Second and perhaps part of the problem with changing the code to `EOF` instead +of `-1` is the `for` loop particularly to do with the bitwise operations, +perhaps also made worse by the seemingly gibberish comments. + +Another important point is that the Makefile compiles the program as +`shapiro_t1` so that the source code that corresponds with it is actually +[shapiro.c](1994/shapiro/shapiro.c). But notice how running `shapiro_t1` results +in different functionality than `shapiro` itself? The source code that +`shapiro_t1` generates, `shapiro_t2.c`, is what is compiled into `shapiro`. # 1995