混淆C $ C $ç大赛2006请解释sykes2.c [英] Obfuscated C Code Contest 2006. Please explain sykes2.c

查看:220
本文介绍了混淆C $ C $ç大赛2006请解释sykes2.c的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请问这个C程序工作?

<$p$p><$c$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:警告:返回类型默认为'诠释'[-Wreturn型]
sykes2.c:在函数'主':
sykes2.c:1:14:警告:​​不使用计算值[-Wunused值]
sykes2.c:1:1:警告:函数'的putchar'隐式声明[-Wimplicit功能声明]
sykes2.c:1:1:警告:在操作数建议各地算术括号'|'[-Wparentheses]
sykes2.c:1:1:警告:在操作数建议各地算术括号'|'[-Wparentheses]
sykes2.c:1:1:警告:控制​​到达非void函数[-Wreturn型]结束


解决方案

让我们去混淆了。

缩进:

 主(_){
    _ ^ 448安培;&安培;主( - 〜_);
    的putchar(--_ 64%
        ? 32 | - 〜7 [__ TIME __-_ / 8%8] - →[48&GT'txiZ ^(〜Z?];&gt;中;;; ====〜$ :: 199[_ * 2及8 | _ / 64] /(_和2 1:8?)%8和1
        :10);
}

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

 的main(int i)以{
    如果(I ^ 448)
        主( - 〜I);
    如果( - 则i%64){
        所以char a = - 〜7 [__ TIME __- I / 8%8] [&GT;'txiZ ^(〜Z? - 48];
        炭B = A&GT;&GT; ;;; ====〜$ 199 ::[我* 2和8 | I / 64] /(I和2?1:8)8%;
        的putchar(32 |(B&安培; 1));
    }其他{
        的putchar(10); // 新队
    }
}

注意 - 〜我== I + 1,因为二进制补码。因此,我们有

 的main(int i)以{
    如果(我!= 448)
        主要第(i + 1);
    一世 - ;
    如果(ⅰ%64 == 0){
        的putchar('\\ n');
    }其他{
        所以char a = - 〜7 [__ TIME __- I / 8%8] [&GT;'txiZ ^(〜Z? - 48];
        炭B = A&GT;&GT; ;;; ====〜$ 199 ::[我* 2和8 | I / 64] /(I和2?1:8)8%;
        的putchar(32 |(B&安培; 1));
    }
}

现在,请注意, A [B] 相同 b [A] ,并应用 - 〜== 1 + 再次更改:

 的main(int i)以{
    如果(我!= 448)
        主要第(i + 1);
    一世 - ;
    如果(ⅰ%64 == 0){
        的putchar('\\ n');
    }其他{
        所以char a =(&GT;'txiZ ^(〜Z? - 48)[(__ TIME __- I / 8%8)[7] + 1;
        炭B = A&GT;&GT; ;;; ====〜$ :: 199[(我* 2及8)|的i / 64] /(I和2 1:8)%8;
        的putchar(32 |(B&安培; 1));
    }
}

递归转换到一个循环,并偷偷在更多的简化:

  //请不要传递任何命令行参数
主(){
    INT I;
    对于(i = 447; I&GT; = 0;我 - ){
        如果(ⅰ%64 == 0){
            的putchar('\\ n');
        }其他{
            焦炭T = __TIME __ [7 - I / 8%8]。
            所以char a =&GT;'txiZ ^(〜Z?[T - 48] + 1;
            INT转变=;;; ====〜$ 199 ::[(我* 2和8)| (I / 64)];
            如果((ⅰ和2)== 0)
                移动/ = 8;
            移移= 8%;
            炭B = A&GT;&GT;转移;
            的putchar(32 |(B&安培; 1));
        }
    }
}

这个输出每次迭代一个字符。每64个字符,它输出一个换行符。否则,它使用一对数据表来找出输出,并提出要么32字符(空格)或字符33(A )。第一个表(&GT;'txiZ ^(〜Z)是一组10位图描述每个角色的外观,和第二个表(;;; ====〜$ 199 ::)选择合适的位从位图显示。

第二个表格

让我们通过检查第二个表, INT偏移开始=;;; ====〜$ 199 ::[(我* 2和8)| (I / 64); I / 64 是行号(6比0)和 I * 2和8 8 IFF​​ I 4,5,6或7 MOD 8。

IF((I和2)== 0)移位/ = 8;移移= 8%或者选择高八进制数字(为 I%8 = 0,1,4,5)或低八进制数字(对于 I%8 = 2,3,6,7)表的价值。这种转变表最终看起来是这样的:

 排山坳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 5月2日至3日
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 6月4日至5日
3月2日至3日5
3 0-1 7
2月6日至7日2
二月四日至五日7
2 2-3 3
2 0-1 7
1月6日至7日2
1 7月4日至五日
1次2-3 3
1 7 0-1
0 6-7 3
0 4-5 4
0 3月2日至三日
0 0-1 7

或以表格形式

  00005577
11775577
11775577
11665577
22773377
22773377
44443377

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

这是一个七段显示器后设计的,与 7 取值为空白。因此,在第一个表中的条目必须定义得到点亮的部分。

第一个表

__ __ TIME 是由preprocessor定义一个特殊的宏。它扩展到包含在其中的preprocessor已运行时间字符串常量,其形式为HH:MM:SS。注意,这正好包含8个字符。需要注意的是有0-9 ASCII值48至57和的ASCII码值58的输出是每行64个字符,使叶片的每个字符8个字符 __ __ TIME

7 - I / 8%8 是这样的指数 __ TIME __ 是presently被输出( 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 能够像空格:

  000055
11 55
11 55
116655
22 33
22 33
444433

因此​​,例如, 4 01101010 (位1,3,5,6集),它打印为

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


要展示我们真正理解code,让我们来调整输出有点与此表:

  00
11 55
11 55
  66
22 33
22 33
  44

这是EN codeD为?;;?==?':: 799 \\ X07。对艺术目的,我们将添加64到几个字符(因为只有低6位,这将不会影响输出);这给?{{}}??GG :: 799G(注意第8字符是未使用的,所以我们实际上可以把它无论我们想要的)。把我们的新表中原来的code:

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

我们得到

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

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

How does this C program work?

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

It compiles as it is (tested on gcc 4.6.3). It prints the time when compiled. On my system:

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

Source: sykes2 - A clock in one line, sykes2 author hints

Some hints: No compile warnings per default. Compiled with -Wall, the following warnings are emitted:

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]

解决方案

Let's de-obfuscate it.

Indenting:

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

Introducing variables to untangle this mess:

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
    }
}

Note that -~i == i+1 because of twos-complement. Therefore, we have

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));
    }
}

Now, note that a[b] is the same as b[a], and apply the -~ == 1+ change again:

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));
    }
}

Converting the recursion to a loop and sneaking in a bit more simplification:

// 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));
        }
    }
}

This outputs one character per iteration. Every 64th character, it outputs a newline. Otherwise, it uses a pair of data tables to figure out what to output, and puts either character 32 (a space) or character 33 (a !). The first table (">'txiZ^(~z?") is a set of 10 bitmaps describing the appearance of each character, and the second table (";;;====~$::199") selects the appropriate bit to display from the bitmap.

The second table

Let's start by examining the second table, int shift = ";;;====~$::199"[(i*2&8) | (i/64)];. i/64 is the line number (6 to 0) and i*2&8 is 8 iff i is 4, 5, 6 or 7 mod 8.

if((i & 2) == 0) shift /= 8; shift = shift % 8 selects either the high octal digit (for i%8 = 0,1,4,5) or the low octal digit (for i%8 = 2,3,6,7) of the table value. The shift table ends up looking like this:

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

or in tabular form

00005577
11775577
11775577
11665577
22773377
22773377
44443377

Note that the author used the null terminator for the first two table entries (sneaky!).

This is designed after a seven-segment display, with 7s as blanks. So, the entries in the first table must define the segments that get lit up.

The first table

__TIME__ is a special macro defined by the preprocessor. It expands to a string constant containing the time at which the preprocessor was run, in the form "HH:MM:SS". Observe that it contains exactly 8 characters. Note that 0-9 have ASCII values 48 through 57 and : has ASCII value 58. The output is 64 characters per line, so that leaves 8 characters per character of __TIME__.

7 - i/8%8 is thus the index of __TIME__ that is presently being output (the 7- is needed because we are iterating i downwards). So, t is the character of __TIME__ being output.

a ends up equalling the following in binary, depending on the input t:

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

Each number is a bitmap describing the segments that are lit up in our seven-segment display. Since the characters are all 7-bit ASCII, the high bit is always cleared. Thus, 7 in the segment table always prints as a blank. The second table looks like this with the 7s as blanks:

000055  
11  55  
11  55  
116655  
22  33  
22  33  
444433  

So, for example, 4 is 01101010 (bits 1, 3, 5, and 6 set), which prints as

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


To show we really understand the code, let's adjust the output a bit with this table:

  00  
11  55
11  55
  66  
22  33
22  33
  44

This is encoded as "?;;?==? '::799\x07". For artistic purposes, we'll add 64 to a few of the characters (since only the low 6 bits are used, this won't affect the output); this gives "?{{?}}?gg::799G" (note that the 8th character is unused, so we can actually make it whatever we want). Putting our new table in the original code:

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

we get

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

just as we expected. It's not as solid-looking as the original, which explains why the author chose to use the table he did.

这篇关于混淆C $ C $ç大赛2006请解释sykes2.c的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆