混淆C代码大赛2006请解释sykes2.c(Obfuscated C Code Contest 2

2019-07-21 13:45发布

请问这个C程序工作?

main(_){_^448&&main(-~_);putchar(--_%64?32|-~7[__TIME__-_/8%8][">'txiZ^(~z?"-48]>>";;;====~$::199"[_*2&8|_/64]/(_&2?1:8)%8&1:10);}

它编译,因为它是(上测试gcc 4.6.3 )。 在编译时它打印的时间。 在我的系统:

    !!  !!!!!!              !!  !!!!!!              !!  !!!!!! 
    !!  !!  !!              !!      !!              !!  !!  !! 
    !!  !!  !!              !!      !!              !!  !!  !! 
    !!  !!!!!!    !!        !!      !!    !!        !!  !!!!!! 
    !!      !!              !!      !!              !!  !!  !! 
    !!      !!              !!      !!              !!  !!  !! 
    !!  !!!!!!              !!      !!              !!  !!!!!!

来源: sykes2 -在一行中的时钟 , sykes2笔者提示

一些提示:没有编译每默认警告。 编译-Wall ,下面的警告发出:

sykes2.c:1:1: warning: return type defaults to ‘int’ [-Wreturn-type]
sykes2.c: In function ‘main’:
sykes2.c:1:14: warning: value computed is not used [-Wunused-value]
sykes2.c:1:1: warning: implicit declaration of function ‘putchar’ [-Wimplicit-function-declaration]
sykes2.c:1:1: warning: suggest parentheses around arithmetic in operand of ‘|’ [-Wparentheses]
sykes2.c:1:1: warning: suggest parentheses around arithmetic in operand of ‘|’ [-Wparentheses]
sykes2.c:1:1: warning: control reaches end of non-void function [-Wreturn-type]

Answer 1:

让我们去混淆它。

缩进:

main(_) {
    _^448 && main(-~_);
    putchar(--_%64
        ? 32 | -~7[__TIME__-_/8%8][">'txiZ^(~z?"-48] >> ";;;====~$::199"[_*2&8|_/64]/(_&2?1:8)%8&1
        : 10);
}

引入变量来解开这个烂摊子:

main(int i) {
    if(i^448)
        main(-~i);
    if(--i % 64) {
        char a = -~7[__TIME__-i/8%8][">'txiZ^(~z?"-48];
        char b = a >> ";;;====~$::199"[i*2&8|i/64]/(i&2?1:8)%8;
        putchar(32 | (b & 1));
    } else {
        putchar(10); // newline
    }
}

需要注意的是-~i == i+1 ,因为二进制补码。 因此,我们有

main(int i) {
    if(i != 448)
        main(i+1);
    i--;
    if(i % 64 == 0) {
        putchar('\n');
    } else {
        char a = -~7[__TIME__-i/8%8][">'txiZ^(~z?"-48];
        char b = a >> ";;;====~$::199"[i*2&8|i/64]/(i&2?1:8)%8;
        putchar(32 | (b & 1));
    }
}

现在,请注意, a[b]是一样的b[a]并应用-~ == 1+再次发生变化:

main(int i) {
    if(i != 448)
        main(i+1);
    i--;
    if(i % 64 == 0) {
        putchar('\n');
    } else {
        char a = (">'txiZ^(~z?"-48)[(__TIME__-i/8%8)[7]] + 1;
        char b = a >> ";;;====~$::199"[(i*2&8)|i/64]/(i&2?1:8)%8;
        putchar(32 | (b & 1));
    }
}

递归转换到一个循环,并在多一点简化鬼鬼祟祟:

// please don't pass any command-line arguments
main() {
    int i;
    for(i=447; i>=0; i--) {
        if(i % 64 == 0) {
            putchar('\n');
        } else {
            char t = __TIME__[7 - i/8%8];
            char a = ">'txiZ^(~z?"[t - 48] + 1;
            int shift = ";;;====~$::199"[(i*2&8) | (i/64)];
            if((i & 2) == 0)
                shift /= 8;
            shift = shift % 8;
            char b = a >> shift;
            putchar(32 | (b & 1));
        }
    }
}

此输出每次迭代一个字符。 每64个字符,它输出一个换行符。 否则,它使用一对数据表找出输出,并把任一字符32(空格)或字符图33(a ! )。 第一个表( ">'txiZ^(~z?" )是描述每个字符的外观的一组10位图,并且所述第二表( ";;;====~$::199" )选择适当的位来从位图显示。

第二个表

让我们通过检查第二个表,启动int shift = ";;;====~$::199"[(i*2&8) | (i/64)]; int shift = ";;;====~$::199"[(i*2&8) | (i/64)];i/64是行号(6至0)和i*2&8是当且仅当8 i是4,5,6或7模8。

if((i & 2) == 0) shift /= 8; shift = shift % 8 if((i & 2) == 0) shift /= 8; shift = shift % 8选择所述高八进制数字(为i%8 = 0,1,4,5)或低八进制数字(为i%8 = 2,3,6,7)表中的值的。 移位表最终看起来是这样的:

row col val
6   6-7 0
6   4-5 0
6   2-3 5
6   0-1 7
5   6-7 1
5   4-5 7
5   2-3 5
5   0-1 7
4   6-7 1
4   4-5 7
4   2-3 5
4   0-1 7
3   6-7 1
3   4-5 6
3   2-3 5
3   0-1 7
2   6-7 2
2   4-5 7
2   2-3 3
2   0-1 7
1   6-7 2
1   4-5 7
1   2-3 3
1   0-1 7
0   6-7 4
0   4-5 4
0   2-3 3
0   0-1 7

或以表格形式

00005577
11775577
11775577
11665577
22773377
22773377
44443377

请注意,笔者使用的空终止前两个表项(偷偷摸摸的!)。

这是一个七段显示器之后设计的,以7 S作为空白。 因此,在第一个表中的条目必须定义得到点亮的段。

第一个表

__TIME__是由预处理器定义的特殊宏。 它扩展为包含在该预处理器被运行时的字符串常量,其形式为"HH:MM:SS" 。 注意,它正好包含8个字符。 需要注意的是有0-9 ASCII值48〜57和:具有ASCII值58.输出是每行64个字符,使叶片的每字符8个字符__TIME__

7 - i/8%8因而是的索引__TIME__当前正输出( 7-需要,因为我们迭代i向下)。 所以, t是的字符__TIME__被输出。

a最终等于二进制以下,根据输入t

0 00111111
1 00101000
2 01110101
3 01111001
4 01101010
5 01011011
6 01011111
7 00101001
8 01111111
9 01111011
: 01000000

每个数字是描述在我们的七段显示器照亮了段的位图 。 由于字符都是7位ASCII,高总是被清零。 因此, 7段表始终打印作为空白。 第二个表看起来像这样与7 S作为空白:

000055  
11  55  
11  55  
116655  
22  33  
22  33  
444433  

因此,例如, 401101010 (位1,3,5,和6组),其作为打印

----!!--
!!--!!--
!!--!!--
!!!!!!--
----!!--
----!!--
----!!--

为了显示我们真正了解的代码,让我们来调整输出有点与此表:

  00  
11  55
11  55
  66  
22  33
22  33
  44

这被编码为"?;;?==? '::799\x07" 。 对于艺术而言,我们会增加64几个字符(因为只有低6位被使用,这会不会影响输出); 这给了"?{{?}}?gg::799G" (注意,第8字符是未使用的,所以我们实际上可以把它我们想要的一切)。 把我们的新表在原来的代码:

main(_){_^448&&main(-~_);putchar(--_%64?32|-~7[__TIME__-_/8%8][">'txiZ^(~z?"-48]>>"?{{?}}?gg::799G"[_*2&8|_/64]/(_&2?1:8)%8&1:10);}

我们得到

          !!              !!                              !!   
    !!  !!              !!  !!  !!  !!              !!  !!  !! 
    !!  !!              !!  !!  !!  !!              !!  !!  !! 
          !!      !!              !!      !!                   
    !!  !!  !!          !!  !!      !!              !!  !!  !! 
    !!  !!  !!          !!  !!      !!              !!  !!  !! 
          !!              !!                              !!   

正如我们的预期。 这不是为固地看着原来,这解释了为什么笔者选择了用他做的表。



Answer 2:

让我们来格式化这个以方便您阅读:

main(_){
  _^448&&main(-~_);
  putchar((--_%64) ? (32|-(~7[__TIME__-_/8%8])[">'txiZ^(~z?"-48]>>(";;;====~$::199")[_*2&8|_/64]/(_&2?1:8)%8&1):10);
}

因此,不带参数运行它,_(ARGC传统)为1main()将递归调用本身,传递的结果-(~_)负位NOT的_ ),所以实际上它会去448个递归(唯一的条件,其中_^448 == 0 )。

服用的是,它会打印7 64字符宽的线(外侧三元条件,和448/64 == 7 )。 因此,让我们把它改写干净了一点:

main(int argc) {
  if (argc^448) main(-(~argc));
  if (argc % 64) {
    putchar((32|-(~7[__TIME__-argc/8%8])[">'txiZ^(~z?"-48]>>(";;;====~$::199")[argc*2&8|argc/64]/(argc&2?1:8)%8&1));
  } else putchar('\n');
}

现在, 32是十进制的ASCII空格。 它或者打印空格或“!” (图33是“!”,因此“ &1 ”处的端部)。 让我们集中在中间的斑点:

-(~(7[__TIME__-argc/8%8][">'txiZ^(~z?"-48]) >>
     (";;;====~$::199"[argc*2&8|argc/64]) / (argc&2?1:8) % 8

作为另一个海报说, __TIME__是编译时该程序,并且是一个字符串,所以有一些字符串算术继续,以及利用一个数组下标是双向的:一个并[b]是相同的苯并[a用于字符阵列。

7[__TIME__ - (argc/8)%8]

这将选择前8个字符的一个__TIME__ 。 然后这被索引到[">'txiZ^(~z?"-48]字符是48-57十进制)在此字符串的字符必须已经被选择为他们的ASCII值这相同的字符的ASCII码操作继续通过表达,导致无论是“”或“的印刷!”这取决于字符的字形中的位置。



Answer 3:

添加到其他的解决方案, -~x等于x+1 ,因为~x是相当于(0xffffffff-x) 这等于(-1-x)在2的补,所以-~x-(-1-x) = x+1



Answer 4:

我去混淆模算术,就像我能和删除reccursion

int pixelX, line, digit ;
for(line=6; line >= 0; line--){
  for (digit =0; digit<8; digit++){
    for(pixelX=7;pixelX > 0; pixelX--){ 
        putchar(' '| 1 + ">'txiZ^(~z?"["12:34:56"[digit]-'0'] >> 
          (";;;====~$::199"[pixel*2 & 8  | line] / (pixelX&2 ? 1 : 8) ) % 8 & 1);               
    }
  }
  putchar('\n');
}

拓展多一点:

int pixelX, line, digit, shift;
char shiftChar;
for(line=6; line >= 0; line--){
    for (digit =0; digit<8; digit++){
        for(pixelX=7;pixelX >= 0; pixelX--){ 
            shiftChar = ";;;====~$::199"[pixelX*2 & 8 | line];
            if (pixelX & 2)
                shift = shiftChar & 7;
            else
                shift = shiftChar >> 3;     
            putchar(' '| (">'txiZ^(~z?"["12:34:56"[digit]-'0'] + 1) >> shift & 1 );
        }

    }
    putchar('\n');
}


文章来源: Obfuscated C Code Contest 2006. Please explain sykes2.c