난독 화 된 C 코드 콘테스트 2006. sykes2.c를 설명하십시오.


975

이 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]

6
디버그 : 인쇄물 printf("%d", _);시작에 추가 main: pastebin.com/HHhXAYdJ
corny

정수, 모든 형식화되지 않은 변수의 기본값은 다음과 같습니다.int
drahnr

18
힌트를 읽었습니까? ioccc.org/2006/sykes2/hint.text
nhahtdh


다음과 같이 실행하면 충돌이 발생합니다../a.out $(seq 0 447)
SS Anne

답변:


1819

난독 화하자.

들여 쓰기 :

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]동일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));
        }
    }
}

반복 당 하나의 문자를 출력합니다. 64 번째 문자마다 줄 바꿈을 출력합니다. 그렇지 않으면, 한 쌍의 데이터 테이블을 사용하여 출력 할 내용을 파악하고 문자 32 (공백) 또는 문자 33 (a !)을 넣습니다 . 첫 번째 테이블 ( ">'txiZ^(~z?")은 각 문자의 모양을 설명하는 10 개의 비트 맵 세트이며, 두 번째 테이블 ( ";;;====~$::199")은 비트 맵에서 표시 할 적절한 비트를 선택합니다.

두 번째 테이블

두 번째 테이블 인을 살펴 보자 int shift = ";;;====~$::199"[(i*2&8) | (i/64)];. i/64행 번호 (6 ~ 0)이고 i*2&88 iff i는 4, 5, 6 또는 7 mod 8입니다.

if((i & 2) == 0) shift /= 8; shift = shift % 8테이블 값 의 상위 8 진수 ( i%80,1,4,5) 또는 하위 8 진수 ( i%82,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

저자는 처음 두 테이블 항목에 대해 null 종결자를 사용했습니다 (sneaky!).

이것은 7 세그먼트 디스플레이 후에 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

각 숫자는 7 세그먼트 디스플레이에서 점등되는 세그먼트를 설명 하는 비트 맵 입니다. 문자는 모두 7 비트 ASCII이므로 높은 비트는 항상 지워집니다. 따라서 7세그먼트 테이블에서 항상 공백으로 인쇄합니다. 두 번째 테이블은 7s를 공백으로 사용하여 다음과 같이 보입니다 .

000055  
11  55  
11  55  
116655  
22  33  
22  33  
444433  

따라서, 예를 들어, 4이다 01101010(비트 1, 3, 5, 6 세트), 인쇄 등

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

코드를 실제로 이해하고 있음을 보여주기 위해 다음 표를 사용하여 출력을 약간 조정 해 보겠습니다.

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

우리는 얻는다

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

우리가 예상 한대로. 저자가 자신이 한 테이블을 사용하기로 선택한 이유를 설명하는 것은 원본보다 견고하지 않습니다.


2
@drahnr-기술적으로 그것은 *(참조 +
취소

18
@ АртёмЦарионов : 약 30 분 정도 걸렸지 만 다시 돌아와서 편집했습니다. 내가 많이 C를 사용하고 내가 전에 개인적인 관심에 대한 몇 가지 IOCCC의 deobfuscations 했어 (내가 한 마지막을 단지 개인의 관심이었다 아름다운 광선 추적기 ). 그것이 어떻게 작동하는지 물어보고 싶다면, 기꺼이 기꺼이;)
nneonneo

5
@ АртёмЦарионов : 하루 IIRC 정도 (레이 트레이서 형상을 이해하는 데 걸린 시간도 포함). 이 프로그램은 키워드를 사용하지 않기 때문에 매우 영리 합니다 .
nneonneo

178
C .. 어셈블리 언어의 가독성과 결합 된 어셈블리 언어의 모든 힘
wim

6
이 맥락에서 더 자세한 내용은 Don Libes의“Obfuscated C and Other Mysteries”를 확인하십시오. 난독 화 된 C 콘테스트 항목을 분석하여 C 기술을 학습합니다.
Chris N

102

더 쉽게 읽을 수 있도록이 형식을 지정하십시오.

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

따라서 인수없이 실행하면 _ (argc는 일반적으로)입니다 1. main()재귀 적으로 호출하고 결과를 -(~_)(음수 비트 NOT의 _) 결과를 전달 하므로 실제로 448 회귀가 발생합니다 (조건 만 _^448 == 0).

그것을 취하면, 64 개의 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');
}

이제 32ASCII 공간의 십진수입니다. 공백이나 '!'를 인쇄합니다. (33은 '!'이므로 &1끝에 ' ')입니다. 가운데의 얼룩에 초점을 맞추겠습니다.

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

다른 포스터가 말했듯 __TIME__이 프로그램의 컴파일 시간이며 문자열이므로 문자열 산술이 진행되고 배열 첨자를 양방향으로 활용하는 것이 좋습니다. a [b]는 b [a]와 같습니다 문자 배열 용.

7[__TIME__ - (argc/8)%8]

에서의 처음 8 자 중 하나를 선택 __TIME__합니다. 그런 다음 색인화됩니다 [">'txiZ^(~z?"-48](0-9 문자는 48-57 소수). 이 문자열의 문자는 ASCII 값으로 선택되어 있어야합니다. 동일한 문자 ASCII 코드 조작이 표현식을 통해 계속되어 ''또는 '!'가 인쇄됩니다. 캐릭터 글리프 내의 위치에 따라


49

다른 솔루션에 더하는 것은와 -~x같으 x+1므로 ~x같습니다 (0xffffffff-x). 이것은 동일 (-1-x)하므로, 2 초 보완에 -~x있다 -(-1-x) = x+1.


5
흥미 롭군 나는 ~ x == -x-1이라고 오랫동안 알고 있었지만 그 뒤에 수학적 추론을 알지 못했습니다.
ApproachingDarknessFish

3
콜, (-1-x)는 (-x-1)과 같습니다. "고정"할 필요가 없습니다 !!
Thomas Song

7
같은 이유-누군가가 -1338 인 경우 1337이 아닙니다.
Andrew Mao

4

나는 모듈로 산술을 난독 화하고 재귀를 제거했습니다.

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

조금 더 확장 :

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');
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.