Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
287 views
in Technique[技术] by (71.8m points)

c - 混淆的C代码竞赛2006.请解释sykes2.c(Obfuscated C Code Contest 2006. Please explain sykes2.c)

How does this C program work?

(这个C程序如何工作?)

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 ).

(它按原样编译(在gcc 4.6.3上测试)。)

It prints the time when compiled.

(它打印编译时的时间。)

On my system:

(在我的系统上:)

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

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

(资料来源: sykes2 - 一行中的一个时钟sykes2作者提示)

Some hints: No compile warnings per default.

(一些提示:默认情况下没有编译警告。)

Compiled with -Wall , the following warnings are emitted:

(使用-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]
  ask by corny translate from so

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.

(注意-~i == i+1因为二进制补码。)

Therefore, we have

(因此,我们有)

main(int i) {
    if(i != 448)
        main(i+1);
    i--;
    if(i % 64 == 0) {
        putchar('
');
    } 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:

(现在,请注意a[b]b[a]相同 ,并再次应用-~ == 1+更改:)

main(int i) {
    if(i != 448)
        main(i+1);
    i--;
    if(i % 64 == 0) {
        putchar('
');
    } 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('
');
        } 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.

(每64个字符,它输出一个换行符。)

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 ! ).

(否则,它使用一对数据表来确定要输出的内容,并放置字符32(空格)或字符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.

(第一个表( ">'txiZ^(~z?" )是一组描述每个字符外观的10个位图,第二个表( ";;;====~$::199" )选择从位图显示的适当位。)

The second table (第二个表)

Let's start by examining the second table, int shift = ";;;====~$::199"[(i*2&8) | (i/64)];

(让我们从检查第二个表开始, int shift = ";;;====~$::199"[(i*2&8) | (i/64)];) 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.

(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 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.

(if((i & 2) == 0) shift /= 8; shift = shift % 8选择所述高八进制数字(为i%8 = 0,1,4,5)或低八进制数字(为i%8 = 2,3,6,7)表中的值的。)

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!).

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

This is designed after a seven-segment display, with 7 s as blanks.

(这是在七段显示器之后设计的,其中7秒为空白。)

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.

(__TIME__是预处理器定义的特殊宏。)

It expands to a string constant containing the time at which the preprocessor was run, in the form "HH:MM:SS" .

(它扩展为包含预处理器运行时间的字符串常量,格式为"HH:MM:SS" 。)

Observe that it contains exactly 8 characters.

(注意它包含正好8个字符。)

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__ .

(请注意,0-9具有ASCII值48到57,并且:具有ASCII值58.每行输出64个字符,因此每个字符__TIME__留下8个字符。)

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).

(7 - i/8%8因此是当前正在输出的__TIME__的索引(需要7-因为我们向下迭代i )。)

So, t is the character of __TIME__ being output.

(所以, t是输出__TIME__的字符。)

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

(a取决于输入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.

(由于字符都是7位ASCII,因此始终清除高位。)

Thus, 7 in the segment table always prints as a blank.

(因此,段表中的7始终作为空白打印。)

The second table looks like this with the 7 s as blanks:

(第二个表看起来像这样, 7秒作为空白:)

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

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

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

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" .

(这被编码为"?;;?==? '::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);

(出于艺术目的,我们将为一些字符添加64(因为只使用低6位,这不会影响输出);)

this gives "?{{?}}?gg::799G" (note that the 8th character is unused, so we can actually make it whatever we want).

(这给出了"?{{?}}?gg::799G" (注意第8个字符未被使用,所以我们实际上可以做任何我们想要的)。)

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.

(它不像原版那样结实,这解释了为什么作者选择使用他所做的表。)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...