这个 1984 年国际混淆 C 代码竞赛获奖作品是如何运作的?

How does this 1984 International Obfuscated C Code Contest winning entry work?

我是 C 的新手,我急切地想知道这段代码的工作原理是什么?

int i;main(){for(;i["]<i;++i){--i;}"];read('-'-'-',i+++"hell\
o, world!\n",'/'/'/'));}read(j,i,p){write(j/p+p,i---j,i/i);}

来源可以在这里找到:http://www.ioccc.org/1984/anonymous.c

下面是代码附带的提示:

Dishonorable mention: Anonymous

The author was too embarrassed that he/she could write such trash, so I promised to protect their identity. I will say that the author of this program has a well known connection with the C programming language. This program is a unique variation on the age old "Hello, world" program. What reads like a read may be written like a write! Copyright (c) 1984, Landon Curt Noll. All Rights Reserved. Permission for personal, educational or non-profit use is granted provided this this copyright and notice are included in its entirety and remains unaltered. All other uses must receive prior permission in writing from both Landon Curt Noll and Larry Bassel.

当您有混淆代码时,您需要清理物理布局,添加一些白色 space,添加必要的缩进,然后编译代码。编译器的警告会告诉你很多关于程序隐藏的一些东西。

第一次切割简化 - 添加白色space

int i;
main()
{
   for( ; i["]<i;++i){--i;}"];
          read('-'-'-', i+++"hell\o, world!\n", '/'/'/'));
}

read(j,i,p)
{
   write(j/p+p,i---j,i/i);
}

当使用 gcc -Wall 编译程序时,我收到以下警告:

soc.c:2:1: warning: return type defaults to ‘int’ [enabled by default]
 main()
 ^
soc.c: In function ‘main’:
soc.c:4:4: warning: implicit declaration of function ‘read’ [-Wimplicit-function-declaration]
    for( ; i["]<i;++i){--i;}"]; read('-'-'-', i+++"hell\o, world!\n", '/'/'/'));
    ^
soc.c:4:50: warning: unknown escape sequence: '\o' [enabled by default]
    for( ; i["]<i;++i){--i;}"]; read('-'-'-', i+++"hell\o, world!\n", '/'/'/'));
                                                  ^
soc.c: At top level:
soc.c:7:1: warning: return type defaults to ‘int’ [enabled by default]
 read(j,i,p)
 ^
soc.c: In function ‘read’:
soc.c:7:1: warning: type of ‘j’ defaults to ‘int’ [enabled by default]
soc.c:7:1: warning: type of ‘i’ defaults to ‘int’ [enabled by default]
soc.c:7:1: warning: type of ‘p’ defaults to ‘int’ [enabled by default]
soc.c:9:4: warning: implicit declaration of function ‘write’ [-Wimplicit-function-declaration]
    write(j/p+p,i---j,i/i);
    ^
soc.c:9:17: warning: operation on ‘i’ may be undefined [-Wsequence-point]
    write(j/p+p,i---j,i/i);
                 ^
soc.c:9:17: warning: operation on ‘i’ may be undefined [-Wsequence-point]
soc.c:10:1: warning: control reaches end of non-void function [-Wreturn-type]
 }
 ^

二切简化-反混淆

根据以上警告,程序可以反混淆为:

int i;

void read(int j, char* i, int p);
void write(int j, char* i, int p);

int main()
{
   for( ; i["]<i;++i){--i;}"];
          read('-'-'-', , '/'/'/'));
   return 0;
}

void read(int j, char* i, int p)
{
   write(j/p+p, (i--) - j, 1);
}

以上版本没有编译器警告并产生相同的输出。

第三次简化 - 取消混淆更多

表达式i["]<i;++i){--i;}"]用于运行循环14次。就那么简单。可以简化为i < 14.

'-'-'-'0.

'/'/'/'1.

i++ + "hello, world!\n"s + i++ 相同,其中 s 可以是 char const* s = "hello, world!\n";

for循环可以简化为:

   char const* s = "hello, world!\n";
   for( ; i < 14; read(0, s+i++, 1));

由于readj的值始终为零,所以read的实现可以简化为:

void read(int j, char* i, int p)
{
   write(0, (i--), 1);
}

表达式 (i--) 可以简化为 i,因为递减作为副作用不会改变函数的工作原理。也就是说,上面的函数是:

void read(int j, char* i, int p)
{
   write(0, i, 1);
}

当我们意识到参数 j 的值总是 0 并且参数 p 的值总是 1 时,我们可以改变 for 在主函数中循环到:

   for( ; i < 14; i++)
   {
      write(0, s+i, 1);
   }

因此,整个程序可以简化为:

void write(int j, char const* i, int p);

int main()
{
   int i = 0;
   char const* s = "hello, world!\n";
   for( ; i < 14; i++ )
   {
      write(0, s+i, 1);
   }
   return 0;
}

第四次简化 - 让它变得平凡

以上版本有一个硬编码编号14。即s中的字符数。因此,通过将程序更改为:

可以使程序变得简单
void write(int j, char const* i, int p);

int main()
{
   write(0, "hello, world!\n", 14);
   return 0;
}

让我们重写一下代码,替换一些表达式并添加注释

int i; // i = 0 by default

main()
{
    for( ;
        "]<i;++i){--i;}" [i];  // magic: it is the same as i["]<i;++i){--i;}"];
                               // the important part is that there's 14 chars in the
                               // string, same length that "hello, world!\n"
                               // the content of the string has no importance here,
                               // in the end 'i' will go from 0 to 14

        read(0, // since '-' - '-' is equal to 0,
             "hello, world!\n" + (i++),  // same as i+++"hello, world!\n"
                                      // it is pointers arythmetic
                                      // the second arg to the read
                                      // function defined further points
                                      // to the letter to print
             1)) // '/' / '/' division result is 1
      ;
}

// the read function is then always called with
// j == 0
// i is a pointer to the char to print
// p == 1
// And for the obfuscation fun, the read function will write something :)
read(j,i,p)
{
    write(0,  // j/p+p is equal to 0, which is the stdout 
          i,  // i-- - j is equal to i, decrement of i comes later
          1);  // i / i is equal to 1, this means one char to print
}