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

cor*_*rny 963 c obfuscation deobfuscation

这个C程序如何工作?

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

它按原样进行编译(测试gcc 4.6.3).它打印编译时的时间.在我的系统上:

    !!  !!!!!!              !!  !!!!!!              !!  !!!!!! 
    !!  !!  !!              !!      !!              !!  !!  !! 
    !!  !!  !!              !!      !!              !!  !!  !! 
    !!  !!!!!!    !!        !!      !!    !!        !!  !!!!!! 
    !!      !!              !!      !!              !!  !!  !! 
    !!      !!              !!      !!              !!  !!  !! 
    !!  !!!!!!              !!      !!              !!  !!!!!!
Run Code Online (Sandbox Code Playgroud)

资料来源: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]
Run Code Online (Sandbox Code Playgroud)

nne*_*neo 1803

让我们去混淆它.

缩进:

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

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

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
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,-~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));
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,请注意与之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));
    }
}
Run Code Online (Sandbox Code Playgroud)

将递归转换为循环并稍微简化一下:

// 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));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

每次迭代输出一个字符.每64个字符,它输出一个换行符.否则,它使用一对数据表来确定要输出的内容,并放置字符32(空格)或字符33(a !).第一个table(">'txiZ^(~z?")是一组描述每个字符外观的10个位图,第二个表(";;;====~$::199")选择要从位图显示的相应位.

第二个表

让我们从检查第二个表开始int shift = ";;;====~$::199"[(i*2&8) | (i/64)];.i/64是行号(6到0),i*2&8是8 iff i是4,5,6或7 mod 8.

if((i & 2) == 0) shift /= 8; shift = shift % 8选择表值的高八进制数字(对于i%8= 0,1,4,5)或低八进制数字(对于= 2,3,6,7)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
Run Code Online (Sandbox Code Playgroud)

或以表格形式

00005577
11775577
11775577
11665577
22773377
22773377
44443377
Run Code Online (Sandbox Code Playgroud)

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

这是在七段显示后设计的,7s为空白.因此,第一个表中的条目必须定义亮起的段.

第一个表

__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
Run Code Online (Sandbox Code Playgroud)

每个数字都是一个位图,用于描述在七段显示中亮起的段.由于字符都是7位ASCII,因此始终清除高位.因此,7在段表中始终打印为空白.第二个表看起来像这样,7s为空白:

000055  
11  55  
11  55  
116655  
22  33  
22  33  
444433  
Run Code Online (Sandbox Code Playgroud)

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

----!!--
!!--!!--
!!--!!--
!!!!!!--
----!!--
----!!--
----!!--
Run Code Online (Sandbox Code Playgroud)

为了表明我们真的理解代码,让我们用这个表调整输出:

  00  
11  55
11  55
  66  
22  33
22  33
  44
Run Code Online (Sandbox Code Playgroud)

这被编码为"?;;?==? '::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);}
Run Code Online (Sandbox Code Playgroud)

我们得到

          !!              !!                              !!   
    !!  !!              !!  !!  !!  !!              !!  !!  !! 
    !!  !!              !!  !!  !!  !!              !!  !!  !! 
          !!      !!              !!      !!                   
    !!  !!  !!          !!  !!      !!              !!  !!  !! 
    !!  !!  !!          !!  !!      !!              !!  !!  !! 
          !!              !!                              !!   
Run Code Online (Sandbox Code Playgroud)

就像我们预期的那样.它不像原版那样结实,这解释了为什么作者选择使用他所做的表.

  • C ..汇编语言的所有强大功能与汇编语言的可读性相结合 (173认同)
  • @АртёмЦарионов:大约30分钟,但我一直回来编辑它.我经常使用C,并且之前为了个人兴趣做了一些IOCCC反混淆(我做的最后一个,仅仅为了个人兴趣,是[这个美丽的光线跟踪](http://www.ioccc.org/2004/) gavare.c)).如果你想问它是如何工作的,我很乐意帮忙;) (18认同)
  • @АртёмЦарионов:大约一天IIRC(也计算了理解光线跟踪器几何的时间).该程序也非常聪明,因为它*不使用关键字*. (5认同)
  • 有关此内容的更多信息,请查看Don Libes撰写的"混淆C和其他未解之谜".它通过分析混淆的C竞赛条目来教授C技术. (5认同)
  • @drahnr - 技术上它既是'*'(取消引用)又是`+`:P (2认同)
  • @ user13267 foo [bar]是通过获取foo的地址并添加bar*sizeof(typeStoredInArray)来获取数组中bar​​th元素的地址来实现的. (2认同)
  • @gath:程序打印出`__TIME__`的值,这是一个具有常量值的宏 - 应用程序编译的时间.它不打算打印当前时间. (2认同)

chm*_*eee 98

我们将其格式化以便于阅读:

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

因此,运行它没有参数,_(传统的argc)是1. main()将递归调用自身,传递-(~_)(负按位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');
}
Run Code Online (Sandbox Code Playgroud)

现在,32ASCII空间是十进制的.它要么打印一个空格,要么打印'!' (33是'!',因此' &1'在最后).让我们关注中间的blob:

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

正如另一张海报所说,__TIME__是程序的编译时间,并且是一个字符串,所以有一些字符串算法正在进行,并且利用数组下标是双向的:a [b]与b [a]相同用于字符数组.

7[__TIME__ - (argc/8)%8]
Run Code Online (Sandbox Code Playgroud)

这将选择前8个字符中的一个__TIME__.然后将其编入索引[">'txiZ^(~z?"-48](0-9个字符为48-57十进制).必须为其ASCII值选择此字符串中的字符.这个相同的字符ASCII代码操作继续通过表达式,导致打印''或'!' 取决于角色字形内的位置.


Tho*_*ong 48

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

  • 同样的原因,如果有人是-1338,那么他们不是1337. (7认同)
  • 有趣.我已经知道了一段时间~x == -x - 1,但我不知道背后的数学推理. (5认同)
  • Ey,Cole,( - 1-x​​)与(-x-1)相同,你不需要"修复"它! (3认同)

Lef*_*s E 6

我尽可能地去混淆了模算术并删除了递归

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');
}
Run Code Online (Sandbox Code Playgroud)

再扩展一点:

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');
}
Run Code Online (Sandbox Code Playgroud)