難読化Cコードコンテスト2006。sykes2.cを説明してください。質問する

難読化Cコードコンテスト2006。sykes2.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: 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]

ベストアンサー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
    }
}

2の補数なので-~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));
        }
    }
}

これは、反復ごとに 1 文字を出力します。64 文字ごとに改行を出力します。それ以外の場合は、データ テーブルのペアを使用して出力する内容を判断し、文字 32 (スペース) または文字 33 ( !) のいずれかを配置します。最初のテーブル ( ">'txiZ^(~z?") は、各文字の外観を記述する 10 個のビットマップのセットであり、2 番目のテーブル ( ";;;====~$::199") は、ビットマップから表示する適切なビットを選択します。

2番目のテーブル

まず、2 番目の表を調べてみましょうint shift = ";;;====~$::199"[(i*2&8) | (i/64)];i/64は行番号 (6 から 0) であり、i*2&88 を法としてi4、5、6、または 7 の場合に限り 8 です。

if((i & 2) == 0) shift /= 8; shift = shift % 8テーブル値の上位 8 進数桁 ( i%8= 0、1、4、5 の場合) または下位 8 進数桁 ( 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

作成者は最初の 2 つのテーブル エントリに null ターミネータを使用していることに注意してください (巧妙です!)。

これは 7 セグメント ディスプレイをベースとして設計されており、7s は空白です。したがって、最初のテーブルのエントリは点灯するセグメントを定義する必要があります。

最初のテーブル

__TIME__は、プリプロセッサによって定義される特別なマクロです。これは、プリプロセッサが実行された時刻を含む文字列定数に展開され、形式は です"HH:MM:SS"。ちょうど 8 文字が含まれていることに注意してください。0 から 9 は ASCII 値 48 から 57 を持ち、 は:ASCII 値 58 を持つことに注意してください。出力は 1 行あたり 64 文字なので、 の 1 文字あたり 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 セグメント ディスプレイで点灯するセグメントを表すビットマップ7です。文字はすべて 7 ビット ASCII なので、上位ビットは常にクリアされます。したがって、セグメント テーブルでは、常に空白として印刷されます。2 番目のテーブルは、s が空白になっている次のようになります7

000055  
11  55  
11  55  
116655  
22  33  
22  33  
444433  

例えば、(ビット1、3、5、6がセットされて4いる)は次のように印刷されます。01101010

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

コードを本当に理解していることを示すために、次の表を使用して出力を少し調整してみましょう。

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

我々が得る

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

予想通りです。オリジナルほどしっかりした見た目ではないので、作者がこのテーブルを使用することを選択した理由がわかります。

おすすめ記事