混淆的C代码竞赛2006。请解释sykes2.c

问题描述 投票:972回答:4

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 - A clock in one linesykes2 author hints

一些提示:默认情况下没有编译警告。编译为-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]
c obfuscation deobfuscation
4个回答
1812
投票

让我们对其进行模糊处理。

缩进:

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] is the same as b[a],然后再次应用a[b]更改:

b[a]

将递归转换为循环并进行进一步简化:

-~ == 1+

每次迭代输出一个字符。每第64个字符,它将输出一个换行符。否则,它将使用一对数据表找出要输出的内容,并放置字符32(空格)或字符33(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)); } } } )是一组10个位图,描述每个字符的外观,第二个表(!)从位图中选择要显示的适当位。

第二张表

让我们开始检查第二张表">'txiZ^(~z?"";;;====~$::199"是行号(6到0),如果int shift = ";;;====~$::199"[(i*2&8) | (i/64)];是4、5、6或7 mod 8,则i/64是8。

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

i%8

或以表格形式

i%8

请注意,作者对前两个表条目使用了空终止符(偷偷摸摸!)。

这是在七段显示器之后设计的,其中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。请注意,它恰好包含8个字符。请注意,0-9的ASCII值从48到57,__TIME__的ASCII值58。输出每行64个字符,因此__TIME__的每个字符保留8个字符。

"HH:MM:SS"因此是当前正在输出的:的索引(需要__TIME__,因为我们向下迭代7 - i/8%8)。因此,__TIME__是正在输出的7-的字符。

i最终等于二进制的以下内容,具体取决于输入t

__TIME__

每个数字是一个位图

,描述在我们的七段显示器中点亮的段。由于所有字符均为7位ASCII,因此始终清除高位。因此,段表中的a始终打印为空白。第二张表看起来像这样,其中t为空白:
0 00111111
1 00101000
2 01110101
3 01111001
4 01101010
5 01011011
6 01011111
7 00101001
8 01111111
9 01111011
: 01000000

因此,例如,77(设置的位1、3、5和6),其打印为

000055  
11  55  
11  55  
116655  
22  33  
22  33  
444433  

为了显示我们对代码的理解,让我们对该表进行一些调整:

4

此编码为01101010。出于艺术目的,我们会将64个字符添加到一些字符中(因为仅使用了低6位,所以这不会影响输出);这给出----!!-- !!--!!-- !!--!!-- !!!!!!-- ----!!-- ----!!-- ----!!-- (请注意,第8个字符尚未使用,因此我们实际上可以根据需要进行设置)。将我们的新表放在原始代码中:

  00  
11  55
11  55
  66  
22  33
22  33
  44

我们得到

"?;;?==? '::799\x07"

正如我们所期望的。它不像原始的一样美观,这解释了为什么作者选择使用他所做的表格。

为了方便阅读,让我们设置此格式:

"?{{?}}?gg::799G"

因此,不带任何参数运行它,_(通常为argc)为main(_){_^448&&main(-~_);putchar(--_%64?32|-~7[__TIME__-_/8%8][">'txiZ^(~z?"-48]>>"?{{?}}?gg::799G"[_*2&8|_/64]/(_&2?1:8)%8&1:10);} !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! 将递归调用自身,传递main(_){ _^448&&main(-~_); putchar((--_%64) ? (32|-(~7[__TIME__-_/8%8])[">'txiZ^(~z?"-48]>>(";;;====~$::199")[_*2&8|_/64]/(_&2?1:8)%8&1):10); } 的结果(1的按位负非),因此实际上它将进行448次递归(仅在main()的情况下)。

[因此,它将打印7个64个字符的宽行(外部三元条件和-(~_))。因此,让我们重写它一点点:

_

现在,_^448 == 0对于ASCII空间为十进制。它会打印一个空格或一个'!' (33是'!',因此最后是'448/64 == 7')。让我们关注中间的blob:

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是程序的编译时间,并且是一个字符串,因此正在进行一些字符串算法,并且利用了数组下标是双向的:a [b]是相同的如b [a]用于字符数组。

&1

这将选择-(~(7[__TIME__-argc/8%8][">'txiZ^(~z?"-48]) >> (";;;====~$::199"[argc*2&8|argc/64]) / (argc&2?1:8) % 8 中的前8个字符之一。然后将其索引为__TIME__(0-9个字符为48-57个十进制)。必须为该字符串中的字符选择其ASCII值。相同的字符ASCII代码操作将贯穿表达式继续进行,从而导致打印出''或'!'。取决于角色字形中的位置。

[除其他解决方案外,7[__TIME__ - (argc/8)%8] 等于__TIME__,因为[">'txiZ^(~z?"-48]等于-~x。这等于2s补码中的x+1,因此~x(0xffffffff-x)

我尽我所能对模算术进行了模糊处理,并消除了递归

(-1-x)

进一步扩展:

-~x

101
投票

为了方便阅读,让我们设置此格式:


49
投票

[除其他解决方案外,7[__TIME__ - (argc/8)%8] 等于__TIME__,因为[">'txiZ^(~z?"-48]等于-~x。这等于2s补码中的x+1,因此~x(0xffffffff-x)


4
投票

我尽我所能对模算术进行了模糊处理,并消除了递归

(-1-x)
© www.soinside.com 2019 - 2024. All rights reserved.