Programing

난독 화 된 C 코드 콘테스트 2006. sykes2.c에 대해 설명해주십시오.

crosscheck 2020. 9. 28. 08:15
반응형

난독 화 된 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]

난독 화를 해제합시다.

들여 쓰기 :

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 i이면 4, 5, 6 또는 7 mod 8입니다.

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

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.


Let's format this for easier reading:

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

So, running it with no arguments, _ (argc conventionally) is 1. main() will recursively call itself, passing the result of -(~_) (negative bitwise NOT of _), so really it'll go 448 recursions (Only condition where _^448 == 0).

Taking that, it'll print 7 64-character wide lines (the outer ternary condition, and 448/64 == 7). So let's rewrite it a little cleaner:

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

Now, 32 is decimal for ASCII space. It either prints a space or a '!' (33 is '!', hence the '&1' at the end). Let's focus on the blob in the middle:

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

As another poster said, __TIME__ is the compile time for the program, and is a string, so there's some string arithmetic going on, as well as taking advantage of an array subscript being bidirectional: a[b] is the same as b[a] for character arrays.

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

This will select one of the first 8 characters in __TIME__. This is then indexed into [">'txiZ^(~z?"-48] (0-9 characters are 48-57 decimal). The characters in this string must have been chosen for their ASCII values. This same character ASCII code manipulation continues through the expression, to result in the printing of either a ' ' or '!' depending on the location within the character's glyph.


Adding to the other solutions, -~x is equal to x+1 because ~x is equivalent to (0xffffffff-x). This is equal to (-1-x) in 2s complement, so -~x is -(-1-x) = x+1.


I de-obfuscated the modulo arithmetics as much as I could and removed the reccursion

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

Expanding it a bit more:

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

참고URL : https://stackoverflow.com/questions/15393441/obfuscated-c-code-contest-2006-please-explain-sykes2-c

반응형