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

问题描述 投票:0回答:2

我是 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

以下是代码附带的提示:

不光彩的提及: 匿名

作者太丢人了,能写出这么垃圾的东西,所以 我答应保护他们的身份。我要说的是作者 该程序与 C 编程有众所周知的联系 语言。这个节目是古老的“你好, world”程序。读起来像读,写起来也像写! 版权所有 (c) 1984,兰登·科特·诺尔。版权所有。允许 允许用于个人、教育或非营利用途,前提是 本版权和声明完整包含并保留 不变。所有其他用途必须事先获得书面许可 来自兰登·科特·诺尔和拉里·巴塞尔。

c deobfuscation
2个回答
15
投票

当您有混淆的代码时,您需要清理物理布局,添加一些空白,添加必要的缩进,然后编译代码。来自编译器的警告会告诉您很多有关程序隐藏的一些内容。

首次简化 - 添加空白

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('-'-'-', i+++"hell\o, world!\n", '/'/'/'));
   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"
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));

由于

j
read
的值始终为零,因此
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;
}

4
投票

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

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
}
© www.soinside.com 2019 - 2024. All rights reserved.