Obfuscated C Code Contest 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
ซ้ำซาก

จำนวนเต็มตัวแปรที่ไม่ได้พิมพ์ทุกค่าเริ่มต้นคือint
drahnr

18
คุณอ่านคำใบ้หรือยัง? ioccc.org/2006/sykes2/hint.text
nhahtdh

2
นอกจากนี้อ่านstackoverflow.com/questions/10321196/…
Xofo

หากคุณเรียกใช้เช่นนี้มันจะขัดข้อง:./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&8คือ 8 iff iคือ 4, 5, 6 หรือ 7 mod 8

if((i & 2) == 0) shift /= 8; shift = shift % 8เลือกตัวเลขฐานแปดสูง (สำหรับi%8= 0,1,4,5) หรือตัวเลขฐานแปดต่ำ (สำหรับ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

โปรดทราบว่าผู้เขียนใช้ null terminator สำหรับสองรายการแรกของตาราง (ส่อเสียด!)

สิ่งนี้ได้รับการออกแบบหลังจากแสดงผลเจ็ดส่วนโดยมี7s เป็นช่องว่าง ดังนั้นรายการในตารางแรกจะต้องกำหนดกลุ่มที่สว่างขึ้น

ตารางแรก

__TIME__เป็นแมโครพิเศษที่กำหนดโดยตัวประมวลผลล่วงหน้า มันจะขยายอย่างต่อเนื่องสตริงที่มีเวลาที่ preprocessor "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

แต่ละหมายเลขเป็นบิตแมปที่อธิบายถึงกลุ่มที่สว่างขึ้นในการแสดงผลเจ็ดส่วนของเรา เนื่องจากอักขระเป็น ASCII 7 บิตทั้งหมดบิตที่สูงจึงถูกล้างเสมอ ดังนั้น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 - ในทางเทคนิคแล้วมันเป็นทั้ง a *(dereference) และ a +: P
detly

18
@ АртёмЦарионов: ประมาณ 30 นาที แต่ฉันกลับมาแล้วและแก้ไขมันอย่างยุติธรรม ฉันใช้ C บ่อยมากและฉันได้ทำการ deobfuscations Deocfuscations เพื่อผลประโยชน์ส่วนตัวก่อนหน้านี้ (อันสุดท้ายที่ฉันทำเพียงเพื่อผลประโยชน์ส่วนตัวคือraytracer ที่สวยงามนี้ ) หากคุณต้องการที่จะถามวิธีการทำงานฉันมีความสุขที่จะบังคับ;)
nneonneo

5
@ АртёмЦарионов: ประมาณหนึ่งวัน IIRC (นับรวมเวลาที่ใช้ในการทำความเข้าใจกับเรขาคณิตของ raytracer) โปรแกรมที่ยังเป็นคนฉลาดมากเพราะมันใช้ไม่มีคำหลัก
nneonneo

178
C .. พลังงานทั้งหมดของภาษาประกอบรวมกับการอ่านของภาษาการชุมนุม
Wim

6
สำหรับข้อมูลเพิ่มเติมในหลอดเลือดดำนี้โปรดดู“ Obfuscated C และ Mysteries อื่น ๆ ” โดย Don Libes มันสอนเทคนิค C โดยการวิเคราะห์รายการประกวด Obfuscated 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()จะเรียกตัวเองซ้ำแล้วซ้ำอีกส่งผ่านผลลัพธ์ของ-(~_)(ค่าลบไม่ใช่ค่าบิต_) ดังนั้นจริงๆแล้วมันจะไปซ้ำ 448 ครั้ง (มีเงื่อนไขเฉพาะที่_^448 == 0)

โดยที่มันจะพิมพ์ 7 บรรทัดกว้าง 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');
}

ตอนนี้32เป็นทศนิยมสำหรับพื้นที่ ASCII มันอาจพิมพ์เว้นวรรคหรือ '!' (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)ในส่วนประกอบ 2s เพื่อให้เป็น-~x-(-1-x) = x+1


5
น่าสนใจ ฉันรู้มาพักหนึ่งแล้วว่า ~ x == -x - 1 แต่ฉันไม่รู้เหตุผลทางคณิตศาสตร์ที่อยู่เบื้องหลัง
ApproachingDarknessFish

3
Ey, Cole (-1-x) เหมือนกับ (-x-1) คุณไม่จำเป็นต้อง "แก้ไข" มัน !!
โธมัสซอง

7
ด้วยเหตุผลเดียวกันว่าทำไมถ้าใครบางคน -1338 แล้วพวกเขาไม่ใช่ 1,337
แอนดรูเหมา

4

ฉัน de-obfuscated modulo arithmetics มากที่สุดเท่าที่จะทำได้และลบการอ้างอิง

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.