รหัสสั้นที่สุดที่จะโยน SIGILL


76

พื้นหลัง

เรามีความท้าทายเกี่ยวกับการขว้าง SIGSEGVแล้วทำไมจึงไม่ท้าทายการขว้าง SIGILL?

SIGILL คืออะไร

SIGILL เป็นสัญญาณสำหรับคำสั่งที่ผิดกฎหมายที่โปรเซสเซอร์ซึ่งเกิดขึ้นน้อยมาก การกระทำเริ่มต้นหลังจากที่ได้รับ SIGILL กำลังยกเลิกโปรแกรมและเขียนข้อมูลหลัก รหัสสัญญาณ SIGILL คือ 4. คุณพบ SIGILL sudo kill -s 4 <pid>น้อยมากและฉันมีความคิดอย่างวิธีการสร้างมันในรหัสของคุณผ่านทางยกเว้นไม่มี

กฎระเบียบ

คุณจะมีรูทในโปรแกรมของคุณ แต่ถ้าคุณไม่ต้องการด้วยเหตุผลใดก็ตามคุณอาจใช้ผู้ใช้ปกติ ฉันใช้คอมพิวเตอร์ Linux ที่มีภาษาเยอรมันและไม่ทราบข้อความภาษาอังกฤษที่ปรากฏหลังจากที่จับ SIGILL ได้ แต่ฉันคิดว่ามันเป็น 'การสอนที่ผิดกฎหมาย' โปรแกรมที่สั้นที่สุดซึ่งทำให้ SIGILL ชนะ


6
คุณอาจต้องการชี้แจงว่าจะต้องสร้างคำสั่งโดยเคอร์เนลหรือไม่ โดยเฉพาะอย่างยิ่งคุณต้องการอนุญาตให้โปรแกรมเพิ่งสร้างโดยตรงโดยใช้การโทร libc raise(SIGILL)หรือไม่

1
Illegal instruction (core dumped)จริงๆมันจะบอกว่า
Erik the Outgolfer

@ ais523 ทุกอย่างได้รับอนุญาต
Mega Man

5
สำหรับฮาร์ดแวร์ใด ๆ ที่สามารถเพิ่ม SIGILL คำตอบจะเหมือนกับความยาวคำสั่ง เพียงแค่วางคำสั่งที่ผิดกฎหมายและพยายามที่จะรัน สิ่งที่น่าสนใจเพียงอย่างเดียวคือ toolchain ที่ซับซ้อนที่เกี่ยวข้อง
OrangeDog

3
* ผู้ที่โพสต์โปรแกรมเก่า ๆ ที่พวกเขาใช้ไม่ได้ *
RudolfJelin

คำตอบ:


112

PDP-11 Assembler (UNIX Sixth Edition), 1 ไบต์

9

คำแนะนำ 9 ไม่ใช่คำสั่งที่ถูกต้องใน PDP-11 (ในฐานแปดมันจะเป็น000011ซึ่งจะไม่ปรากฏในรายการคำแนะนำ (PDF)) แอสเซมเบลอร์ PDP-11 ที่มาพร้อมกับ UNIX Sixth Edition เห็นได้ชัดว่าสะท้อนทุกสิ่งที่ไม่เข้าใจลงในไฟล์โดยตรง ในกรณีนี้ 9 เป็นตัวเลขดังนั้นจึงสร้างคำสั่งตามตัวอักษร 9 นอกจากนี้ยังมีคุณสมบัติแปลก ๆ (ผิดปกติในภาษาแอสเซมบลีทุกวันนี้) ที่ไฟล์เริ่มทำงานตั้งแต่เริ่มต้นดังนั้นเราไม่จำเป็นต้องมีการประกาศใด ๆ งาน.

คุณสามารถทดสอบโปรแกรมโดยใช้ตัวจำลองนี้แม้ว่าคุณจะต้องต่อสู้กับมันเพื่อป้อนโปรแกรม

ต่อไปนี้คือสิ่งต่าง ๆ ที่เกิดขึ้นเมื่อคุณค้นพบวิธีการใช้ระบบไฟล์ตัวแก้ไขเทอร์มินัลและสิ่งที่คล้ายกันซึ่งคุณคิดว่าคุณรู้แล้วว่าจะใช้งานอย่างไร:

% a.out
Illegal instruction -- Core dumped

ฉันยืนยันกับเอกสารว่านี่เป็นSIGILLสัญญาณของแท้(และมันยังมีหมายเลขสัญญาณเดียวกัน 4 ตลอดทาง!)


1
มันมีหมายเลขสัญญาณเหมือนกันเพราะ POSIX และ UNIX และ SUS นั้นมีความสัมพันธ์กันอย่างใกล้ชิด :)
cat

4
เกือบทุกหมายเลขสัญญาณใน V6 ยังคงมีความหมายเหมือนกันในวันนี้ ความจำช่วยจำได้จริงน้อยกว่าตัวเลข เปรียบเทียบminnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/sys/param.hกับgithub.com/freebsd/freebsd/freebd/blob/master/sys/sys/signal.h - ความหมายที่เหมือนกันสำหรับ 1 ถึง 13 แต่มีเพียง 1, 2 และ 13 เท่านั้นที่มีชื่อเหมือนกันทั้งหมด (SIGALRM / 14 และ SIGTERM / 15 ถูกเพิ่มเข้ามาใน V7 เท่านั้น) (ระบบ Line V มีการเปลี่ยนแปลงสองอย่างโดยเฉพาะย้าย SIGBUS จาก 10 เป็น 7 (แทนที่ SIGEMT ที่ไร้ประโยชน์) และ SIGSYS ที่สูงกว่า 15 เพื่อให้มีที่ว่างสำหรับ SIGUSR1 และ SIGUSR2.)
zwol

4
@cat POSIX และ SUS ไม่ได้ระบุค่าของสัญญาณจริง ๆ - พวกมันจะระบุความหมายของตัวเลขบางตัวเมื่อส่งผ่านเป็นอาร์กิวเมนต์ไปยังคำสั่ง kill แต่ SIGILL ไม่รวมอยู่ด้วย
Random832

7
a.outมีหลายไบต์ในนั้นแน่นอน ( 9คำสั่งรวบรวมสองไบต์และแอสเซมเบลอร์ยังเพิ่มส่วนหัวและส่วนท้ายเพื่อให้โปรแกรมปฏิบัติการ) นั่นเป็นเหตุผลที่ฉันเขียนโปรแกรมเป็นภาษาแอสเซมบลีไม่ใช่ในรหัสเครื่อง โปรแกรมภาษาแอสเซมบลีมีเพียงหนึ่งไบต์ในและคอมไพล์โปรแกรมที่มีไบต์เพิ่ม นี่เป็นปัญหาของcode-golf (ลดขนาดของแหล่งที่มา) ไม่ใช่ปัญหา sizecoding (ลดขนาดของ executable) ดังนั้นจึงเป็นขนาด 1 ไบต์ของแหล่งที่มา

2
การใช้ระบบที่เก่า แต่น่ากลัวอย่างชาญฉลาด
เสา

81

C (x86_64, tcc ), 7 ไบต์

main=6;

แรงบันดาลใจจากคำตอบนี้

ลองออนไลน์!

มันทำงานอย่างไร

แอสเซมบลีที่สร้างขึ้นมีลักษณะเช่นนี้

    .globl  main
main:
    .long 6

โปรดทราบว่า TCC ไม่ได้วาง "ฟังก์ชั่น" ที่กำหนดไว้ในส่วนข้อมูล

หลังจากการรวบรวม_startจะชี้ไปที่หลักตามปกติ เมื่อโปรแกรมที่เกิดขึ้นจะถูกดำเนินการคาดว่ารหัสในหลักและพบน้อย endian (!) 32 บิตจำนวนเต็ม6ซึ่งจะถูกเข้ารหัสเป็น0x06 0x00 0x00 0x00 ไบต์แรก - 0x06 - เป็น opcode ที่ไม่ถูกต้องเพื่อให้โปรแกรมสิ้นสุดกับSIGILL


C (x86_64, gcc ), 13 ไบต์

const main=6;

ลองออนไลน์!

มันทำงานอย่างไร

หากไม่มีตัวดัดแปลงconstแอสเซมบลีที่สร้างจะมีลักษณะเช่นนี้

    .globl  main
    .data
main:
    .long   6
    .section    .note.GNU-stack,"",@progbits

ตัวเชื่อมโยงของ GCC ใช้บรรทัดสุดท้ายเป็นคำใบ้ว่าวัตถุที่สร้างขึ้นไม่จำเป็นต้องมีสแต็กที่เรียกใช้งานได้ เนื่องจากmainถูกวางไว้อย่างชัดเจนในส่วนของข้อมูล opcode ที่มีอยู่นั้นไม่สามารถเรียกใช้งานได้ดังนั้นโปรแกรมจะยุติการทำงานSIGSEGV (การแบ่งเซกเมนต์ผิด)

การลบบรรทัดที่สองหรือบรรทัดสุดท้ายจะทำให้งานที่สร้างขึ้นนั้นเป็นไปตามที่ต้องการ บรรทัดสุดท้ายอาจจะไม่สนใจที่มีธงคอมไพเลอร์-zexecstack( ลองออนไลน์! ) แต่ค่าใช้จ่ายนี้12 ไบต์

ทางเลือกที่สั้นกว่าคือการประกาศmainด้วยตัวแก้ไขconstทำให้เกิดแอสเซมบลีต่อไปนี้

        .globl  main
        .section    .rodata
main:
        .long   6
        .section    .note.GNU-stack,"",@progbits

สิ่งนี้ทำงานได้โดยไม่มีแฟล็กคอมไพเลอร์ โปรดทราบว่าmain=6;จะเขียน "ฟังก์ชั่น" ที่กำหนดไว้ในข้อมูลแต่ตัวดัดแปลงconstทำให้ GCC เขียนในRodataแทนซึ่งอย่างน้อย (บนแพลตฟอร์มของฉัน) ได้รับอนุญาตให้มีรหัส


รักการหลีกเลี่ยงอย่างสมบูรณ์แม้กระทั่งการใช้ฟังก์ชั่น :) มันใช้งานได้กับคำศัพท์ C อย่างไร? คอมไพเลอร์เห็นว่าmainเป็น 6 และลองเรียกใช้ (ซึ่งฉันเดาว่าจะทำให้เลิกและลองใช้คำแนะนำ)
Mia yun Ruse

11
@JackDobson มันเป็นพฤติกรรมที่ไม่ได้กำหนดไว้ดังนั้นมันจึงไม่ทำงานในแง่ของ C; คุณอยู่ที่ความเมตตาของคอมไพเลอร์ เสียงดังกราวยังมีคำเตือนด้วยเหตุผลนี้: "ตัวแปรชื่อ 'main' พร้อมการเชื่อมโยงภายนอกมีพฤติกรรมที่ไม่ได้กำหนด"
Bobby Sacamano

2
GCC จะบ่นmainว่าไม่ได้เป็นฟังก์ชัน แต่ถ้าคุณเปิดคำเตือน ( -Wallหรือ-pedanticจะทำอย่างนั้น)
zwol

ฉันคิดว่ามันสวยมาตรฐานสำหรับ executables สำหรับระบบปฏิบัติการ Unix เหมือนจะมีข้อความ / ข้อมูล / BSS กลุ่ม ตัวเชื่อมโยงวาง.rodata ส่วนไว้ในส่วนข้อความของไฟล์ปฏิบัติการและฉันคาดว่านี่จะเป็นกรณีใด ๆ ในทุกแพลตฟอร์ม (โปรแกรมโหลดเดอร์ของเคอร์เนลใส่ใจเฉพาะเซ็กเมนต์ไม่ใช่เฉพาะส่วน)
Peter Cordes

3
และโปรดทราบว่า06เป็นเพียงคำสั่งที่ไม่ถูกต้องใน x86-64 ในโหมด 32 บิตPUSH ESคำตอบนี้ใช้ได้กับคอมไพเลอร์ที่เป็นค่าเริ่มต้น-m64เท่านั้น ดูref.x86asm.net/coder.html#x06 ลำดับไบต์เท่านั้นที่รับประกันการถอดรหัสเป็นคำแนะนำที่ผิดกฎหมายในทุกซีพียู x86 อนาคตเป็น 2 ไบต์UD20F 0B : สิ่งอื่นใดอาจเป็นคำนำหน้าในอนาคตหรือการเข้ารหัสคำสั่ง แต่ถึงกระนั้นก็ยังได้รับการโหวตให้เป็นวิธีที่ยอดเยี่ยมในการรับคอมไพเลอร์ C เพื่อติดmainฉลากบนไบต์บาง!
Peter Cordes

39

Swift, 5 ไบต์

[][0]

เข้าถึงดัชนี 0 ของอาเรย์ที่ว่างเปล่า การโทรนี้fatalError()ซึ่งพิมพ์ข้อความแสดงข้อผิดพลาดและขัดข้องด้วย SIGILL คุณสามารถลองได้ที่นี่


นี่เป็นหนึ่งในกลลวงที่ยิ่งใหญ่กว่า)
Mega Man

26
... ทำไมบนโลกถึงพังกับ SIGILL? ใครคิดว่านั่นเป็นสัญญาณที่เหมาะสม Bloody hipsters: D
Muzer

4
@Asu No; จงใจล้มเหลวโดยการทำงานfatalError() ud2ทำไมพวกเขาเลือกที่จะทำอย่างนั้นฉันไม่รู้ แต่บางทีพวกเขาคิดว่าข้อความแสดงข้อผิดพลาด "คำสั่งที่ผิดกฎหมาย" สมเหตุสมผลเพราะโปรแกรมทำสิ่งผิดกฎหมาย
NobodyNada

2
สุกใส ฉันจะพนันว่านี่เป็นรหัสที่สั้นที่สุดของ Swift ที่ทำให้เกิดความผิดพลาด
JAL

3
@JAL ใช่ ฉันไม่สามารถคิดถึงสิ่งที่สั้นกว่านี้ได้ ฉันพยายามnil!แต่คอมไพเลอร์ไม่สามารถอนุมานประเภทผลลัพธ์ (และสวัสดี JAL!)
NobodyNada

23

GNU C, 25 ไบต์

main(){__builtin_trap();}

GNU C (ภาษาถิ่นของ C พร้อมกับส่วนขยาย) มีคำสั่งให้ขัดข้องโปรแกรมโดยเจตนา การนำไปปฏิบัติที่แน่นอนนั้นแตกต่างกันไปในแต่ละรุ่น แต่บ่อยครั้งที่ผู้พัฒนาพยายามที่จะใช้ระบบล้มเหลวให้ถูกที่สุดเท่าที่จะทำได้ซึ่งโดยปกติจะเกี่ยวข้องกับการใช้คำสั่งที่ผิดกฎหมาย

รุ่นเฉพาะที่ฉันใช้ทดสอบคือgcc (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0; อย่างไรก็ตามโปรแกรมนี้ทำให้ SIGILL บนแพลตฟอมค่อนข้างกว้างและสามารถพกพาได้ นอกจากนี้ยังดำเนินการผ่านคำสั่งที่ผิดกฎหมาย นี่คือแอสเซมบลีโค้ดที่คอมไพล์ด้านบนรวบรวมกับการตั้งค่าการเพิ่มประสิทธิภาพเริ่มต้น:

main:
    pushq %rbp
    movq %rsp, %rbp
    ud2

ud2 เป็นคำสั่งที่ Intel รับประกันว่าจะยังไม่ได้กำหนดเสมอ


11
−6:main(){asm("ud2");}
wchargin

นอกจากนี้ผมไม่ทราบวิธีการที่เรานับไบต์สำหรับการชุมนุมดิบ แต่00 00 0f 0bภาษาเครื่องสำหรับud2...
wchargin

5
@wchargin: นั่นเป็นคำตอบ x86 + GNU C อันนี้เป็นแบบพกพาไปยังระบบ GNU ทั้งหมด นอกจากนี้ทราบว่าUD2 เพียง 2 ไบต์ IDK ที่คุณได้รับ00ไบต์เหล่านั้น พวกเขาไม่ได้เป็นส่วนหนึ่งของรหัสเครื่องสำหรับ UD2 BTW ตามที่ฉันแสดงความคิดเห็นในคำตอบของเดนนิสมีคำแนะนำที่ผิดกฎหมายหนึ่งไบต์ใน x86-64 สำหรับตอนนี้ แต่พวกเขาไม่รับประกันว่าจะยังคงอยู่
Peter Cordes

@wchargin: เรานับจำนวนไบต์สำหรับฟังก์ชั่น / รหัสเครื่องตามที่คุณคาดหวัง เห็นบางส่วนของคำตอบของฉันเช่นAdler32 ใน 32 ไบต์ของรหัสเครื่อง x86-64หรือGCD 8 ไบต์ของรหัสเครื่อง
Peter Cordes

1
@Ruslan: พวกเขาไม่ได้; 00 00ถอดรหัสเหมือนกันใน x86-64 (as add [rax], al) 00 00 0f 0bมักจะ SIGSEGV ก่อน SIGILL ยกเว้นว่าคุณมีตัวชี้ที่เขียนraxได้
Peter Cordes

22

C (x86_64), 11, 30, 34 หรือ 34 + 15 = 49 ไบต์

main[]="/";
c=6;main(){((void(*)())&c)();}
main(){int c=6;((void(*)())&c)();}

ฉันได้ส่งวิธีแก้ไขปัญหาสองข้อที่ใช้ฟังก์ชั่นห้องสมุดเพื่อโยนSIGILLผ่านวิธีการต่าง ๆ แต่เนื้อหานั้นเป็นการโกงโดยที่ฟังก์ชั่นห้องสมุดแก้ปัญหาได้ นี่คือโซลูชันหลากหลายที่ไม่ใช้ฟังก์ชั่นห้องสมุดและตั้งสมมติฐานที่แตกต่างกันเกี่ยวกับตำแหน่งที่ระบบปฏิบัติการยินดีให้คุณรันโค้ดที่ไม่สามารถเรียกใช้งานได้ (ค่าคงที่ที่นี่ถูกเลือกสำหรับ x86_64 แต่คุณสามารถเปลี่ยนให้เป็นวิธีการแก้ปัญหาการทำงานสำหรับโปรเซสเซอร์อื่น ๆ ส่วนใหญ่ที่มีคำแนะนำที่ผิดกฎหมาย)

06เป็นไบต์ต่ำสุดของรหัสเครื่องที่ไม่สอดคล้องกับคำสั่งที่กำหนดไว้ในโปรเซสเซอร์ x86_64 ดังนั้นสิ่งที่เราต้องทำคือเรียกใช้มัน (อีกวิธีหนึ่ง2Fคือยังไม่ได้กำหนดและสอดคล้องกับอักขระ ASCII ที่พิมพ์ได้หนึ่งตัว) ทั้งสองสิ่งนี้รับประกันว่าจะไม่ได้กำหนดเสมอไป แต่จะไม่ได้กำหนดไว้ ณ วันนี้

โปรแกรมแรกที่นี่ทำงาน2Fจากส่วนข้อมูลแบบอ่านอย่างเดียว ตัวเชื่อมโยงส่วนใหญ่ไม่สามารถผลิตข้ามการทำงานจาก.textไปยัง.rodata(หรือเทียบเท่าของระบบปฏิบัติการ) เนื่องจากไม่ใช่สิ่งที่จะมีประโยชน์ในโปรแกรมที่แบ่งกลุ่มอย่างถูกต้อง ฉันยังไม่พบระบบปฏิบัติการที่ใช้งานได้ คุณต้องยอมให้ข้อเท็จจริงที่ว่าคอมไพเลอร์หลายคนต้องการให้สตริงที่เป็นปัญหานั้นเป็นสตริงที่กว้างซึ่งจะต้องมีL; ฉันสมมติว่าระบบปฏิบัติการใด ๆ ที่ใช้งานได้นี้มีมุมมองที่ล้าสมัยไปแล้วและกำลังสร้างมาตรฐานก่อน C94 ตามค่าเริ่มต้น เป็นไปได้ว่าไม่มีที่ไหนที่โปรแกรมนี้ใช้งานได้ แต่ก็เป็นไปได้ว่ามีบางที่ที่โปรแกรมนี้ใช้งานได้และดังนั้นฉันจึงแสดงรายการไว้ในคำตอบที่อาจเป็นไปได้ที่น่าสงสัยมากกว่า (หลังจากที่ผมโพสต์คำตอบนี้เดนนิสยังกล่าวถึงความเป็นไปได้main[]={6}ในการแชทซึ่งเป็นระยะเวลาเดียวกันและที่ไม่ได้ทำงานเป็นปัญหาที่มีความกว้างของตัวละครและแม้กระทั่งนัยที่มีศักยภาพสำหรับmain=6ผมไม่สมควรเรียกร้องคำตอบเหล่านี้เป็น ของฉันเนื่องจากฉันไม่ได้คิดถึงพวกเขาเอง)

โปรแกรมที่สองที่นี่ทำงาน06จากส่วนข้อมูลการอ่าน - เขียน ในระบบปฏิบัติการส่วนใหญ่สิ่งนี้จะทำให้เกิดความผิดพลาดในการแบ่งเซกเมนต์เนื่องจากเซ็กเมนต์ข้อมูลที่เขียนได้ถูกพิจารณาว่าเป็นข้อบกพร่องในการออกแบบที่ไม่ดี แม้ว่ามันจะไม่ได้เป็นอย่างนั้นเสมอไปดังนั้นมันอาจจะใช้ได้กับ Linux เวอร์ชั่นเก่า แต่ฉันไม่สามารถทดสอบมันได้อย่างง่ายดาย

โปรแกรมที่สามดำเนินการ06จากสแต็ก อีกครั้งสิ่งนี้ทำให้เกิดความผิดพลาดในการแบ่งกลุ่มในปัจจุบันเนื่องจากสแตกถูกจัดประเภทเป็นไม่สามารถเขียนได้ด้วยเหตุผลด้านความปลอดภัย เอกสาร linker ที่ฉันเห็นอย่างหนักแสดงให้เห็นว่าเคยถูกกฎหมายในการเรียกใช้งานจาก stack (ซึ่งแตกต่างจากสองกรณีก่อนหน้านี้การทำเช่นนั้นมีประโยชน์บ้าง) ดังนั้นแม้ว่าฉันจะไม่สามารถทดสอบได้ แต่ฉันค่อนข้างแน่ใจว่ามีบางอย่าง เวอร์ชั่นของ Linux (และอาจเป็นระบบปฏิบัติการอื่น ๆ ) ที่ใช้งานได้

ท้ายที่สุดถ้าคุณให้-Wl,-z,execstack(15 โทษไบต์) ถึงgcc(หากใช้ GNU ldเป็นส่วนหนึ่งของแบ็กเอนด์) มันจะปิดการป้องกันสแต็คที่ปฏิบัติการได้อย่างชัดเจนทำให้โปรแกรมที่สามทำงานและให้สัญญาณการทำงานผิดกฎหมายตามที่คาดไว้ ฉันได้ทดสอบและตรวจสอบความถูกต้องของเวอร์ชัน 49- ไบต์แล้ว (เดนนิสกล่าวถึงการแชทว่าตัวเลือกนี้ใช้งานได้main=6ดีซึ่งจะให้คะแนน 6 +15 ฉันประหลาดใจมากที่งานนี้เนื่องจาก 6 ตัวโจ๋งครึ่มไม่ได้อยู่ในสแต็กตัวเลือกการเชื่อมโยงทำมากกว่า ชื่อของมันแนะนำ)


บน x86-64 / linux ที่มี gcc6 ในโหมดเริ่มต้น (ผ่อนปรน) จะconst main=6;ทำงานได้เหมือนหลายรูปแบบ ลิงเกอร์นี้ (ซึ่งผมสงสัยว่ายังเป็นตัวเชื่อมโยงของคุณ) คือความสามารถในการสร้างกระโดดจาก.textไป.rodata; ปัญหาที่คุณมีก็คือโดยที่constคุณไม่ต้องกระโดดเข้าไปในส่วนข้อมูลที่เขียนได้ ( .data) ซึ่งไม่สามารถใช้งานได้กับฮาร์ดแวร์ที่ทันสมัย มันจะทำงานกับ x86 รุ่นเก่าซึ่งฮาร์ดแวร์ป้องกันหน่วยความจำไม่สามารถทำเครื่องหมายหน้าเว็บว่าอ่านได้ แต่ไม่สามารถเรียกใช้งานได้
zwol

โปรดทราบว่าแม้ใน C89 mainจะต้องมีฟังก์ชั่น (§5.1.2.2.1) - ฉันไม่รู้ว่าทำไม gcc พิจารณาว่าการประกาศmainเป็นวัตถุข้อมูลเพื่อให้ได้รับการเตือนเท่านั้นและด้วย-pedanticบรรทัดคำสั่งเท่านั้น บางคนย้อนกลับไปในช่วงต้นทศวรรษ 1990 อาจคิดว่าไม่มีใครทำแบบนั้นโดยบังเอิญ แต่ก็ไม่เหมือนว่ามันมีประโยชน์ที่จะทำในสิ่งที่ต้องทำโดยไม่ได้ตั้งใจยกเว้นเกมประเภทนี้
zwol

1
... อ่านอีกครั้งดูเหมือนว่าคุณคาดว่าmain[]="/"จะข้ามไปยังส่วนข้อมูลแบบอ่านอย่างเดียวเนื่องจากตัวอักษรสตริงไปใน rodata คุณได้รับการติดออกจากความแตกต่างระหว่างและchar *foo = "..." เป็นน้ำตาลเชิงประโยคสำหรับดังนั้นสตริงตัวอักษรจะไปใน Rodata และเป็นตัวแปรทั่วโลกที่แยกได้เขียนได้ที่ชี้ไปที่มัน ด้วยแต่อาร์เรย์ทั้งหมดไปในส่วนของข้อมูลที่สามารถเขียนได้ char foo[] = "..."char *foo = "..."const char __inaccessible1[] = "..."; char *foo = (char *)&__inaccessible1[0];foochar foo[] = "..."
zwol

19

GNU เป็น (x86_64), 3 ไบต์

ud2

$ xxd sigill.S

00000000: 7564 32                                  ud2

$ เป็น --64 sigill.S -o sigill.o; ld -S sigill.o -o sigill

sigill.S: Assembler messages:
sigill.S: Warning: end of file not at end of a line; newline inserted
ld: warning: cannot find entry symbol _start; defaulting to 0000000000400078

$ ./sigill

Illegal instruction

$ objdump -d sigill

sigill:     file format elf64-x86-64

Disassembly of section .text:

0000000000400078 <__bss_start-0x200002>:>
  400078:       0f 0b                   ud2

จะต้องมีวิธีการแสดงว่าใน "แหล่ง" สองไบต์ ...
OrangeDog

โอ้ฉลาด ฉันสงสัยว่ามีวิธีการสร้างสิ่งนี้หรือไม่ซึ่งจะทำให้จุดเข้าใช้งานเริ่มต้นของไฟล์ (โดยไม่มีการประกาศ) และจะไม่ต้องเสียค่าปรับสำหรับการกำหนดค่าระบบการสร้างที่ผิดปกติ ฉันหาไม่เจอ แต่ดูเหมือนว่าคุณจะทำ

@ ais523: ใช่การสร้างสคริปต์ NASM / YASM ปกติของฉัน ( asm-link)สำหรับโปรแกรมเล่นไฟล์เดียวจะสร้างไฟล์ที่เรียกใช้งานได้จากแหล่งนี้ในลักษณะเดียวกันเนื่องจากldเริ่มต้นที่จุดเริ่มต้นไปยังจุดเริ่มต้นของส่วนข้อความหรืออะไรทำนองนั้น ฉันไม่คิดว่าจะเป็นแหล่งที่มาขนาด asm ในนี้: P
Peter Cordes

18

Bash บน Raspbian ใน QEMU ไบต์ 4 (1?)

ไม่ใช่งานของฉัน ฉันแค่รายงานงานของคนอื่น ฉันไม่ได้อยู่ในฐานะที่จะทดสอบการอ้างสิทธิ์ได้ เนื่องจากส่วนสำคัญของความท้าทายนี้ดูเหมือนว่ากำลังค้นหาสภาพแวดล้อมที่สัญญาณนี้จะถูกเพิ่มและจับผมไม่ได้รวมขนาดของ QEMU, Raspbian หรือทุบตี

ในวันที่ 27 กุมภาพันธ์ 2013 เวลา 20:49 น. ผู้ใช้emlhalacรายงานว่า "ได้รับ 'คำสั่งที่ผิดกฎหมาย' เมื่อพยายามจะ chroot " บน Raspberry Pi fora

ping

การผลิต

qemu: uncaught target signal 4 (Illegal instruction) - core dumped
Illegal instruction (core dumped)

trผมคิดว่าคำสั่งที่สั้นมากจะผลิตออกนี้ตัวอย่างเช่น

แก้ไข: ตามความคิดเห็นของ @ fluffyลดขอบเขตล่างของความยาวอินพุตให้เป็น "1"


5
ฉันคิดว่า[คำสั่งจะชนะ :)
ปุย

17

x86 ไฟล์ MS-DOS COM ขนาด 2 ไบต์

แก้ไข:ตามที่ระบุไว้ในความคิดเห็น DOS เองจะไม่ดักจับข้อยกเว้นของ CPU และจะแขวน (ไม่ใช่แค่แอพระบบปฏิบัติการทั้งหมด) การใช้งานบนระบบปฏิบัติการ 32 บิต NT เช่น Windows XP จะก่อให้เกิดสัญญาณการสอนที่ผิดกฎหมาย

0F 0B

จากเอกสาร :

สร้าง opcode ที่ไม่ถูกต้อง คำแนะนำนี้มีไว้สำหรับการทดสอบซอฟต์แวร์เพื่อสร้าง opcode ที่ไม่ถูกต้องอย่างชัดเจน

ซึ่งค่อนข้างอธิบายตนเองได้ บันทึกเป็นไฟล์. com และเรียกใช้ในโปรแกรมจำลองการทำงานของ DOS emulator ของ DOS จะผิดพลาด ทำงานบน Windows XP, Vista หรือ 7 32 บิต

SIGILL บน Windows XP


1
ในทางเทคนิคแล้วจะทำให้โปรเซสเซอร์ประมวลผลเพื่อสร้างข้อยกเว้นคำสั่งที่ผิดกฎหมาย อย่างไรก็ตาม DOS มีการป้องกันหน่วยความจำที่ จำกัด มากและความสามารถในการจัดการข้อยกเว้นและฉันจะไม่แปลกใจถ้ามันนำไปสู่พฤติกรรมที่ไม่ได้กำหนด / ระบบปฏิบัติการล่ม OP ไม่ได้บอกว่าเคอร์เนลต้องจับข้อผิดพลาดและพิมพ์ "Illegal Instruction" ไปยังคอนโซล
maservant

4
ฉันคิดถึงวิธีนี้ แต่ฉันไม่คิดว่ามันจะใช้ได้ คำถามนั้นต้องการสัญญาณการเรียนการสอนที่ผิดกฎหมายไม่ใช่แค่กับตัวประมวลผลคำสั่งที่ผิดกฎหมายดังนั้นจุดประสงค์คือการหาระบบปฏิบัติการที่จะสร้างสัญญาณจริงเพื่อตอบสนองกับ#UDดัก (นอกจากนี้ฉันตัดสินใจที่จะทดสอบจริง ๆ และดูเหมือนจะโยนตัวจำลอง DOS ของฉันลงในลูปแบบไม่ จำกัด )

2
ฉันทดสอบสิ่งนี้กับ MS-DOS จริงบน AMD K6-II การรันเพียงแฮงค์ระบบทั้งที่มีและไม่มี EMM386 กำลังทำงานอยู่ (EMM386 ดักข้อผิดพลาดบางอย่างและหยุดระบบด้วยข้อความดังนั้นมันจึงคุ้มค่าที่จะทดสอบว่ามันสร้างความแตกต่างหรือไม่)
Mark

2
ใช่ windows-based ของ DOS ควรจะสามารถดักกับดักได้และอย่างน้อยก็ทำให้แอพล่ม
Asu

2
@Asu ฉันสามารถยืนยันได้ว่าทำงานได้กับ XP 32- บิตและอาจใช้ได้กับระบบ Windows 32 บิตอื่นทั้งหมด ฉันยอมรับว่านี่ไม่ใช่วิธีแก้ปัญหาสำหรับ DOS เพราะมันเพิ่งจะล่ม
maservant

13

C (Windows 32 บิต), 34 ไบต์

f(i){(&i)[-1]-=9;}main(){f(2831);}

ใช้งานได้เฉพาะเมื่อรวบรวมโดยไม่มีการเพิ่มประสิทธิภาพ (อื่นรหัสที่ผิดกฎหมายในfฟังก์ชั่นคือ "เพิ่มประสิทธิภาพออก")

การถอดชิ้นส่วนของmainฟังก์ชั่นมีลักษณะดังนี้:

68 0f 0b 00 00    push 0b0f
e8 a1 d3 ff ff    call _f
...

เราจะเห็นว่ามันใช้pushคำสั่งที่มีค่าตามตัวอักษร0b0f(little-endian ดังนั้น bytes จะถูกสลับ) callคำแนะนำและผลักดันที่อยู่ผู้ส่ง (ของ...การเรียนการสอน) ซึ่งตั้งอยู่ในกองอยู่ใกล้กับพารามิเตอร์ของฟังก์ชัน โดยใช้[-1]รางฟังก์ชั่นการแทนที่ที่อยู่กลับมาเพื่อที่จะชี้ 9 ไบต์ก่อนหน้านี้ที่ไบต์ที่0f 0bมี

ไบต์เหล่านี้ทำให้เกิดข้อยกเว้น "คำสั่งที่ไม่ได้กำหนด" ตามที่ออกแบบไว้


12

Java, 50 43 24 ไบต์

a->a.exec("kill -4 $$");

นี่คือjava.util.function.Consumer<Runtime>1มีคำสั่งถูกขโมยจากคำตอบของปุย มันใช้งานได้เพราะคุณต้องเรียกมันว่าwhateverNameYouGiveIt.accept(Runtime.getRuntime())!

โปรดทราบว่าสิ่งนี้จะสร้างกระบวนการใหม่และทำให้มันเป็น SIGILL แทนที่จะขว้าง SIGILL ด้วยตัวเอง

1 - ในทางเทคนิคแล้วมันอาจจะเป็นjava.util.function.Function<Runtime, Process>เพราะRuntime#exec(String)คืนค่าjava.lang.Processซึ่งสามารถใช้ในการควบคุมกระบวนการที่คุณเพิ่งสร้างขึ้นโดยการดำเนินการคำสั่งเชลล์


เพื่อประโยชน์ในการทำสิ่งที่น่าประทับใจมากขึ้นในภาษา verbose นี่คือโบนัส72 60 48- ไบต์:

a->for(int b=0;;b++)a.exec("sudo kill -s 4 "+b);

อันนี้เป็นอีกอันConsumer<Runtime>ที่ผ่านกระบวนการทั้งหมด (รวมถึงตัวมันเอง) ทำให้แต่ละอันโยน SIGILL รั้งที่ดีขึ้นสำหรับความผิดพลาดที่รุนแรง


และโบนัสอื่น (a Consumer<ANYTHING_GOES>) ซึ่งอย่างน้อยก็แกล้งโยน SIGILL ใน 20 ไบต์:

a->System.exit(132);

8

Perl, 9 ไบต์

kill+4,$$

เพียงเรียกใช้ฟังก์ชันไลบรารีที่เหมาะสมสำหรับการส่งสัญญาณกระบวนการและให้โปรแกรมส่งสัญญาณด้วยSIGILLตนเอง ไม่มีคำแนะนำที่ผิดกฎหมายเกิดขึ้นที่นี่ แต่จะให้ผลลัพธ์ที่เหมาะสม (ฉันคิดว่าสิ่งนี้ทำให้ความท้าทายค่อนข้างถูก แต่ถ้ามีอะไรที่ได้รับอนุญาตนี่คือช่องโหว่ที่คุณใช้ ... )


+มาที่นี่เพื่อโพสต์เดียวกันกับพื้นที่แทนได้ :)
simbabque

2
เมื่อผู้คนเรียนรู้ Perl +สำหรับการเขียนโปรแกรมที่ไม่ใช่กอล์ฟพวกเขาเรียนรู้ด้วย หลังจากเล่นกอล์ฟไปสักพักหนึ่งแล้วพวกเขา+จะอวดบ้างเป็นครั้งคราว ในที่สุดพวกเขาได้เขียนโปรแกรมเพียงพอที่พวกเขาต้องการหลีกเลี่ยงช่องว่างด้วยเหตุผลบางอย่างหรืออื่น ๆ ที่+กลายเป็นนิสัย (นอกจากนี้ยังแยกวิเคราะห์น้อยกว่าเลศนัยเพราะมันทำงานรอบ ๆ เรียกกรณีพิเศษใน parser สำหรับวงเล็บ.)

8

ARM Unified Assembler Language (UAL), 3 ไบต์

nop

ตัวอย่างเช่น:

$ as ill.s -o ill.o
$ ld ill.o -o ill
ld: warning: cannot find entry symbol _start; defaulting to 00010054
$ ./ill 
Illegal instruction

หลังจากดำเนินnopการประมวลผลตีความ.ARM.attributesส่วนเป็นรหัสและพบคำสั่งที่ผิดกฎหมายบางแห่งที่นั่น:

$ objdump -D ill

ill:     file format elf32-littlearm


Disassembly of section .text:

00010054 <__bss_end__-0x10004>:
   10054:       e1a00000        nop                     ; (mov r0, r0)

Disassembly of section .ARM.attributes:

00000000 <.ARM.attributes>:
   0:   00001341        andeq   r1, r0, r1, asr #6
   4:   61656100        cmnvs   r5, r0, lsl #2
   8:   01006962        tsteq   r0, r2, ror #18
   c:   00000009        andeq   r0, r0, r9
  10:   01080106        tsteq   r8, r6, lsl #2

ทดสอบกับราสเบอร์รี่ Pi 3


ยังไงก็เถอะมันใช้ไม่ได้กับ Pi 2
Mega Man

7

Microsoft C (Visual Studio 2005 เป็นต้นไป), 16 ไบต์

main(){__ud2();}

ฉันไม่สามารถทดสอบสิ่งนี้ได้อย่างง่ายดาย แต่จากเอกสารประกอบมันควรสร้างคำสั่งที่ผิดกฎหมายโดยจงใจพยายามเรียกใช้คำสั่งเคอร์เนลเท่านั้นจากโปรแกรมโหมดผู้ใช้ (โปรดทราบว่าเนื่องจากคำสั่งที่ผิดกฎหมายทำให้โปรแกรมขัดข้องเราไม่จำเป็นต้องลองกลับมาmainซึ่งหมายความว่าmainฟังก์ชั่นK & R นี้ใช้ได้ถูกต้อง Visual Studio ที่ไม่เคยย้ายจาก C89 นั้นเป็นสิ่งที่ไม่ดี มีประโยชน์ที่นี่)


คุณสามารถรวบรวมสำหรับ linux กับ VS2015 ได้ไหม? เพราะฉันไม่คิดว่า SIGILL กำหนดไว้ใน Windows ใช่หรือไม่
Andrew Savinykh

7

ทับทิมขนาด 13 ไบต์

`kill -4 #$$`

ฉันเดาว่ามันปลอดภัยที่จะสมมติว่าเรากำลังเรียกใช้งานจากเปลือกหอย ตัวอักษร backtick รันคำสั่งเชลล์ที่กำหนด $$เป็นกระบวนการ Ruby ที่กำลังทำงานอยู่และ#สำหรับการแก้ไขสตริง


โดยไม่ต้องเรียกเชลล์โดยตรง:

ทับทิมขนาด 17 ไบต์

Process.kill 4,$$

6

เชลล์ใด ๆ (sh, bash, csh ฯลฯ ), POSIX ใด ๆ (10 ไบต์)

คำตอบเล็กน้อย แต่ฉันไม่เห็นใครโพสต์เลย

kill -4 $$

เพียงส่ง SIGILL ไปยังกระบวนการปัจจุบัน ตัวอย่างเอาต์พุตบน OSX:

bash-3.2$ kill -4 $$
Illegal instruction: 4

คุณสามารถทำได้kill -4 1หากคำถามนั้นไม่เฉพาะเจาะจงเกี่ยวกับโปรแกรมที่จะโยน SIGILL
Mark K Cowan

@MarkKCowan Heh ชี้ดี แต่ที่ถือว่าราก ...
ปุย

2
You will have root in your programs- เคาะไบต์คำตอบของคุณและหมุนคำถามเล็กน้อยในเวลาเดียวกัน: D โบนัส: คุณต้องฆ่าinit
Mark K Cowan

2
@ MarkKCowan: เปิด (รุ่นที่ทันสมัย) Linux initจริง ๆ แล้วมีภูมิคุ้มกันที่จะส่งสัญญาณว่าไม่ได้รับการร้องขอเป็นพิเศษแม้แต่กับรูท คุณอาจจะหลีกเลี่ยงปัญหานี้ได้ด้วยการใช้ POSIX OS อื่น

2
kill -4 2แล้ว: D
Mark K Cowan

6

รหัสเครื่อง ELF + x86 ขนาด 45 ไบต์

นี่ควรเป็นโปรแกรมเรียกทำงานที่เล็กที่สุดในเครื่อง Unix ที่พ่น SIGILL (เนื่องจาก Linux ไม่รู้จักการปฏิบัติการหากทำน้อยกว่านี้)

คอมไพล์ด้วยnasm -f bin -o a.out tiny_sigill.asmทดสอบบนเครื่องเสมือน x64

จริง 45 ไบต์ไบนารี่:

0000000 457f 464c 0001 0000 0000 0000 0000 0001

0000020 0002 0003 0020 0001 0020 0001 0004 0000

0000040 0b0f c031 cd40 0080 0034 0020 0001

รายการประกอบ (ดูแหล่งที่มาด้านล่าง):

;tiny_sigill.asm      
BITS 32


            org     0x00010000

            db      0x7F, "ELF"             ; e_ident
            dd      1                                       ; p_type
            dd      0                                       ; p_offset
            dd      $$                                      ; p_vaddr 
            dw      2                       ; e_type        ; p_paddr
            dw      3                       ; e_machine
            dd      _start                  ; e_version     ; p_filesz
            dd      _start                  ; e_entry       ; p_memsz
            dd      4                       ; e_phoff       ; p_flags


_start:
                ud2                             ; e_shoff       ; p_align
                xor     eax, eax
                inc     eax                     ; e_flags
                int     0x80
                db      0
                dw      0x34                    ; e_ehsize
                dw      0x20                    ; e_phentsize
                db      1                       ; e_phnum
                                                ; e_shentsize
                                                ; e_shnum
                                                ; e_shstrndx

  filesize      equ     $ - $$

ข้อความปฏิเสธความรับผิดชอบ: รหัสจากบทช่วยสอนต่อไปนี้เกี่ยวกับการเขียนโปรแกรมชุดประกอบที่เล็กที่สุดเพื่อส่งคืนตัวเลข แต่ใช้ opcode ud2 แทน mov: http://www.muppetlabs.com/~breadbox/software/tiny/teensy.html


2
ฉันจะโพสต์ไฟล์ปฏิบัติการที่แก้ไขแล้วของบทช่วยสอนที่แน่นอน แต่คุณเอาชนะฉันได้ นี่สมควรที่จะชนะ; มันเป็นความเรียบง่ายที่แท้จริงในแง่ของทรัพยากรระบบ (ต้องไม่เกิน 45 ไบต์ทั้งในไฟล์และในหน่วยความจำและเหมือนกันทั้งคู่) และไม่มีล่ามป่องเหมือนโซลูชันแอสเซมเบลอร์อื่น ๆ มันเป็นสิ่งที่มันเป็น
Iwillnotexist Idonotexist

5

AutoIt , 93 ไบต์

การใช้ชุดประกอบแบบอินไลน์ flatassembler:

#include<AssembleIt.au3>
Func W()
_("use32")
_("ud2")
_("ret")
EndFunc
_AssembleIt("int","W")

เมื่อทำงานในโหมดโต้ตอบ SciTE มันจะพังทันที ตัวดีบักของ Windows ควรป๊อปอัปเป็นเสี้ยววินาที เอาต์พุตคอนโซลจะเป็นดังนี้:

--> Press Ctrl+Alt+Break to Restart or Ctrl+Break to Stop
0x0F0BC3
!>14:27:09 AutoIt3.exe ended.rc:-1073741795

ที่ไหน-1073741795เป็นรหัสข้อผิดพลาดที่ไม่ได้กำหนดโยนโดย WinAPI นี่อาจเป็นจำนวนลบ

คล้ายกันโดยใช้แอสเซมเบลอร์LASMของฉัน:

#include<LASM.au3>
$_=LASM_ASMToMemory("ud2"&@CRLF&"ret 16")
LASM_CallMemory($_,0,0,0,0)

5

NASM ขนาด 25 ไบต์

ฉันไม่รู้ว่ามันใช้งานได้ดีแค่ไหนบนคอมพิวเตอร์ของฉันโดยเฉพาะ (Linux x86_64)

global start
start:
jmp 0

รวบรวมและเรียกใช้เช่น:

$ nasm -f elf64 ill.asm && ld ill.o && ./a.out
ld: warning: cannot find entry symbol _start; defaulting to 0000000000400080
Illegal instruction

คุณสามารถตัดให้สั้นลงเหลือสี่ไบต์เช่นja 0
Mark K Cowan

1
หรือสามเป็นud2
Mark K Cowan

1
อาจเป็นเพราะมันพยายามที่จะข้ามไปยังข้อมูลบางส่วน?
Asu

5

แอสเซมบลี Hex ของ TI-83 2 ไบต์

PROGRAM:I
:AsmPrgmED77

Asm(prgmI)ทำงานเป็น ดำเนินการ opcode 0xed77 ที่ผิดกฎหมาย ฉันนับเลขฐานสิบหกแต่ละคู่เป็นหนึ่งไบต์





1

GNU C, 24 19 18 ไบต์

-4 ขอบคุณเดนนิส
-1 ขอบคุณแมวบนเพดาน

main(){goto*&"'";}

ลองออนไลน์! สิ่งนี้ถือว่า ASCII และ x86_64 พยายามเรียกใช้รหัสเครื่อง27ซึ่ง ... ผิดกฎหมาย


shortC , 10 5 4 ไบต์

AV"'

เทียบเท่ากับรหัส GNU C ด้านบน ลองออนไลน์!


L"\6"ก็ผิดกฎหมายสมมติว่า x86_64
เดนนิส

@Dennis คำสั่ง 0x06 คืออะไร? นอกจากนี้ยังLไม่จำเป็น
MD XF

มันไม่ได้กำหนด
Dennis

มันไม่ได้กำหนด; นั่นคือสิ่งที่ทำให้มันผิดกฎหมาย นอกจากนี้ยัง'เป็น39 = 0x27ไม่0x39
Dennis

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.