Фон
У нас уже есть проблема броска SIGSEGV , так почему бы не бросить вызов SIGILL?
Что такое SIGILL?
SIGILL - сигнал о недопустимой инструкции на процессоре, что происходит очень редко. Действие по умолчанию после получения SIGILL - завершение программы и запись дампа ядра. Идентификатор сигнала SIGILL равен 4. Вы встречаетесь с SIGILL очень редко, и я абсолютно не знаю, как сгенерировать его в вашем коде, кроме как через sudo kill -s 4 <pid>
.
правила
Вы будете иметь root в своих программах, но если вы не хотите по каким-либо причинам, вы также можете использовать обычного пользователя. Я на компьютере Linux с немецким языком, и я не знаю английский текст, который отображается после перехвата SIGILL, но я думаю, что это что-то вроде «Недопустимая инструкция». Самая короткая программа, которая бросает SIGILL, побеждает.
raise(SIGILL)
?Illegal instruction (core dumped)
.Ответы:
Ассемблер PDP-11 (UNIX Sixth Edition), 1 байт
Инструкция 9 не является действительной инструкцией для PDP-11 (в восьмеричном виде она будет
000011
отсутствовать в списке инструкций (PDF)). Ассемблер PDP-11, поставляемый с шестым выпуском UNIX, по-видимому, выводит все, что он не понимает, непосредственно в файл; в этом случае 9 - это число, поэтому он генерирует буквальную инструкцию 9. Он также имеет свойство odd (необычное в настоящее время на языках ассемблера), что файлы запускаются с самого начала, поэтому нам не нужны никакие объявления для создания программы. работай.Вы можете протестировать программу с помощью этого эмулятора , хотя вам придется несколько бороться с ним, чтобы ввести программу.
Вот как все закончится, когда вы поймете, как использовать файловую систему, редактор, терминал и тому подобные вещи, которые, как вы думали, вы уже знаете, как использовать:
В документации я подтвердил, что это подлинный
SIGILL
сигнал (и он даже имел тот же номер сигнала, 4, тогда!)источник
a.out
действительно, содержит несколько байтов (9
инструкция компилируется в два байта, и ассемблер также добавляет верхний и нижний колонтитулы, чтобы сделать программу исполняемой). Вот почему я написал программу на ассемблере, а не в машинном коде. Программа на языке ассемблера имеет только один байт и компилируется в программу с большим количеством байтов; это проблема кода-гольфа (минимизировать размер источника), а не проблема кодирования размера (минимизировать размер исполняемого файла), поэтому имеет значение размер источника в 1 байт.C (x86_64, tcc ), 7 байтов
Вдохновлен этим ответом .
Попробуйте онлайн!
Как это устроено
Сгенерированная сборка выглядит следующим образом.
Обратите внимание, что TCC не помещает определенную «функцию» в сегмент данных .
После компиляции _start будет указывать на main как обычно. Когда получающаяся в результате программа выполняется, она ожидает код main и находит 32-разрядное целое число с прямым порядком байтов (!) 6 , которое кодируется как 0x06 0x00 0x00 0x00 . Первый байт - 0x06 - недопустимый код операции, поэтому программа завершается с SIGILL .
C (x86_64, гкц ), 13 байтов
Попробуйте онлайн!
Как это устроено
Без модификатора const сгенерированная сборка выглядит следующим образом.
Линкер GCC обрабатывает последнюю строку как подсказку о том, что сгенерированный объект не требует исполняемого стека. Поскольку main явно размещается в разделе данных , содержащийся в нем код операции не является исполняемым, поэтому программа завершит работу SIGSEGV (ошибка сегментации).
Удаление второй или последней строки заставит сгенерированный исполняемый файл работать так, как задумано. Последняя строка может быть проигнорирована с флагом компилятора
-zexecstack
( попробуйте онлайн! ), Но это стоит 12 байтов .Более короткая альтернатива - объявить main с модификатором const , что приведет к следующей сборке.
Это работает без каких-либо флагов компилятора. Обратите внимание, что
main=6;
в данные будет записана определенная «функция» , но модификатор const вместо этого заставит GCC записать его в родату , которая (по крайней мере, на моей платформе) может содержать код.источник
main
это 6, и пытается вызвать его (что, я думаю, заставило бы его сдаться и попробовать инструкцию)?main
не является функцией , но только если вы включите предупреждения (либо-Wall
или-pedantic
будет делать это)..rodata
раздел внутри текстового сегмента исполняемого файла, и я ожидаю, что это будет иметь место практически на любой платформе. (Загрузчик программ ядра заботится только о сегментах, а не о разделах).06
это только недопустимая инструкция в x86-64. В 32-битном режиме это такPUSH ES
, поэтому этот ответ работает только с компиляторами по умолчанию-m64
. См. Ref.x86asm.net/coder.html#x06 . Единственный байт последовательность , которая гарантированно декодировать как недопустимую инструкцию на всех будущих процессоров x86 является 2 байта UD2 :0F 0B
. Все остальное может быть каким-то будущим префиксом или кодировкой инструкций. Тем не менее, проголосовал за классный способ заставить компилятор C прикрепитьmain
метку на несколько байтов!Swift, 5 байт
Доступ к индексу 0 пустого массива. Это вызывает
fatalError()
, который печатает сообщение об ошибке и падает с SIGILL. Вы можете попробовать это здесь .источник
fatalError()
намеренно вылетает при бегеud2
. Почему они решили сделать это, я не знаю, но, возможно, они думали, что сообщение об ошибке «Недопустимая инструкция» имело смысл, потому что программа сделала что-то нелегальное.nil!
, но компилятор не мог определить тип результата. (Кроме того, привет JAL!)GNU C, 25 байт
GNU C (определенный диалект C с расширениями) содержит инструкцию преднамеренного сбоя программы. Точная реализация варьируется от версии к версии, но часто разработчики пытаются реализовать сбой настолько дешево, насколько это возможно, что обычно включает использование недопустимой инструкции.
Конкретная версия , которую я использовал , чтобы проверить это
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0
; однако, эта программа вызывает SIGILL на довольно широком диапазоне платформ, и, таким образом, является довольно переносимой. Кроме того, он делает это посредством фактического выполнения недопустимой инструкции. Вот код сборки, который компилируется выше с настройками оптимизации по умолчанию:ud2
это инструкция, которую Intel гарантирует, всегда будет оставаться неопределенным.источник
main(){asm("ud2");}
00 00 0f 0b
это машинный язык дляud2
...00
байты; они не являются частью машинного кода для UD2. Кстати, как я прокомментировал ответ Денниса, на данный момент в x86-64 есть однобайтовые недопустимые инструкции, но они не гарантируют того, что так и останется.00 00
декодирует то же самое в x86-64 (какadd [rax], al
).00 00 0f 0b
обычно SIGSEGV до SIGILL, если только у вас не было записываемого указателяrax
.C (x86_64), 11, 30, 34 или 34 + 15 = 49 байтов
Я представил пару решений, которые используют библиотечные функции для броска
SIGILL
различными способами, но, возможно, это обман, поскольку библиотечная функция решает проблему. Вот ряд решений, которые не используют библиотечные функции и делают различные предположения о том, где операционная система готова позволить вам выполнять неисполняемый код. (Константы здесь выбраны для x86_64, но вы можете изменить их, чтобы получить рабочие решения для большинства других процессоров с недопустимыми инструкциями.)06
является байтом машинного кода с наименьшим номером, который не соответствует определенной инструкции на процессоре x86_64. Так что все, что нам нужно сделать, это выполнить его. (В качестве альтернативы,2F
также не определено и соответствует одному печатному символу ASCII.) Ни один из них не всегда гарантированно не определен, но на сегодняшний день они не определены.Первая программа здесь выполняется
2F
из сегмента данных только для чтения. Большинство линкеров не способны произвести рабочий переход от.text
к.rodata
(или эквивалент их ОС) , поскольку это не то , что когда - нибудь будет полезно в правильно сегментированной программе; Я еще не нашел операционную систему, в которой это работает. Вы также должны учитывать тот факт, что многие компиляторы хотят, чтобы рассматриваемая строка была широкой строкой, что потребовало бы дополнительнойL
; Я предполагаю, что любая операционная система, на которой это работает, имеет довольно устаревшее представление и, таким образом, по умолчанию использует стандарт, предшествующий C94. Возможно, что нигде не работает эта программа, но также возможно, что где-то эта программа работает, и поэтому я перечисляю ее в этой коллекции потенциальных сомнительных-менее-сомнительных потенциальных ответов. (После того, как я опубликовал этот ответ, Деннис также упомянул возможностьmain[]={6}
в чате, которая имеет такую же длину, и которая не сталкивается с проблемами ширины символов, и даже намекала на вероятность этогоmain=6
; я не могу обоснованно утверждать, что эти ответы как мой, так как я сам о них не думал.)Вторая программа здесь выполняется
06
из сегмента данных чтения-записи. В большинстве операционных систем это вызовет ошибку сегментации, поскольку сегменты данных с возможностью записи считаются плохим недостатком проекта, который делает возможными эксплойты. Однако это не всегда так, поэтому он, вероятно, работает на достаточно старой версии Linux, но я не могу легко это проверить.Третья программа выполняется
06
из стека. Опять же, это вызывает ошибку сегментации в наше время, потому что стек обычно классифицируется как недоступный для записи по соображениям безопасности. Документация по компоновщику, которую я видел, в значительной степени подразумевает, что он был законным для выполнения из стека (в отличие от двух предыдущих случаев, иногда это полезно), поэтому, хотя я не могу это проверить, я почти уверен, что есть некоторые версия Linux (и, возможно, другие операционные системы), на которой это работает.Наконец, если вы даете
-Wl,-z,execstack
(15-байтовое наказание)gcc
(если используете GNUld
как часть бэкэнда), это явно отключит защиту исполняемого стека, позволяя третьей программе работать и, как и ожидалось, подать сигнал недопустимой операции. Я уже испытано и проверено это 49-байтовый версия для работы. (Деннис упоминает в чате, что эта опция, очевидно, работаетmain=6
, что дает оценку 6 + 15. Я очень удивлен, что это работает, учитывая, что 6 явно не в стеке; опция ссылки, очевидно, делает больше, чем его название подсказывает.)источник
const main=6;
работает, как и несколько вариантов. Этот линкер (я подозреваю , тоже ваш компоновщик) является способным генерировать скачок от.text
до.rodata
; проблема, с которой вы столкнулись, заключается в том, что безconst
этого вы попадаете в доступный для записи сегмент данных (.data
), который не может быть выполнен на современном оборудовании. Это работало бы на более старых x86, где аппаратное обеспечение защиты памяти не могло маркировать страницы как читаемые, но не исполняемые.main
обязательно должна быть функция (§5.1.2.2.1) - я не знаю, почему gcc рассматривает объявлениеmain
в качестве объекта данных только для того, чтобы заслужить только предупреждение, и только-pedantic
в командной строке. Кто-то в начале 1990-х, возможно, думал, что никто не сделает это случайно, но это не так, как будто это полезно делать специально, за исключением игры такого рода.main[]="/"
перейти к сегменту данных только для чтения, потому что строковые литералы идут в родате. Вы были пойманы разницей междуchar *foo = "..."
иchar foo[] = "..."
.char *foo = "..."
является синтаксическим сахаром дляconst char __inaccessible1[] = "..."; char *foo = (char *)&__inaccessible1[0];
, поэтому строковый литерал идет в родате иfoo
является отдельной доступной для записи глобальной переменной, указывающей на него. Приchar foo[] = "..."
этом весь массив помещается в сегмент данных для записи.GNU as (x86_64), 3 байта
$ xxd sigill.S
$ as --64 sigill.S -o sigill.o; ld -S sigill.o -o sigill
$ ./sigill
$ objdump -d sigill
источник
asm-link
) для однофайловых игрушечных программ будет создавать исполняемый файл из этого источника таким же образом, поскольку поld
умолчанию точка входа находится в начале текстового сегмента или что-то в этом роде. Я просто не думал о том, чтобы выбрать размер исходного кода asm: PBash на Raspbian на QEMU, 4 (1?) Байта
Не моя работа. Я просто сообщаю о работе другого. Я даже не в состоянии проверить претензию. Поскольку важной частью этой проблемы, похоже, является нахождение среды, в которой этот сигнал будет поднят и пойман, я не учитываю размер QEMU, Raspbian или bash.
27 февраля 2013 г. в 20:49 пользователь emlhalac сообщил на форуме « Raspberry Pi » « Получение« недопустимых инструкций при попытке выполнить chroot ».
производства
Я полагаю , гораздо более короткие команды будут производить этот вывод, например,
tr
.РЕДАКТИРОВАТЬ: На основе комментария @ fluffy , уменьшил предполагаемую нижнюю границу длины ввода до «1?».
источник
[
команда победит. :)COM-файл x86 MS-DOS, 2 байта
РЕДАКТИРОВАТЬ: Как указано в комментариях, сама DOS не будет перехватывать исключение процессора и будет просто зависать (не только приложение, вся ОС). Работа в 32-битной операционной системе на базе NT, такой как Windows XP, действительно вызовет недопустимый сигнал инструкции.
Из документации :
Что довольно очевидно. Сохраните как файл .com и
запустите в любом эмулятореDOS. Эмуляторы DOS просто рухнут. Работать на Windows XP, Vista или 7 32-разрядных.источник
#UD
ловушку. (Кроме того, я решил протестировать его, и он, похоже, бросил мой эмулятор DOS в бесконечный цикл.)C (32-битная Windows), 34 байта
Это работает только при компиляции без оптимизации (в противном случае недопустимый код в
f
функции «оптимизирован»).Разборка
main
функции выглядит так:Мы можем видеть, что он использует
push
инструкцию с буквальным значением0b0f
(little-endian, поэтому его байты меняются местами).call
Инструкция выталкивает адрес возврата (в...
инструкции), который находится в стеке около параметра функции. Используя[-1]
смещение, функция переопределяет адрес возврата, поэтому она указывает на 9 байтов раньше, где байты0f 0b
находятся.Эти байты вызывают исключение «неопределенная инструкция», как и было задумано.
источник
Java,
504324 байтаЭто
java.util.function.Consumer<Runtime>
1, чья команда украдена из ответа Пушистика . Это работает, потому что вы должны называть это какwhateverNameYouGiveIt.accept(Runtime.getRuntime())
!Обратите внимание, что это создаст новый процесс и заставит его бросать SIGILL, а не сам SIGILL.
1 - Технически это также может быть
java.util.function.Function<Runtime, Process>
потому, чтоRuntime#exec(String)
возвращает a,java.lang.Process
который можно использовать для управления процессом, который вы только что создали, выполнив команду оболочки.Ради делать что - то более впечатляющее таким многословным языком, вот
7260бонус 48 байт:Этот - другой,
Consumer<Runtime>
который проходит ВСЕ процессы (включая себя), заставляя каждый из них бросать SIGILL. Лучше подготовиться к насильственной аварии.И еще один бонус (а
Consumer<ANYTHING_GOES>
), который, по крайней мере, претендует на то, чтобы бросить SIGILL в 20 байтов:источник
Perl, 9 байт
Просто вызывает соответствующую библиотечную функцию для сигнализации процесса и заставляет программу сигнализировать себя
SIGILL
. Здесь нет никаких фактических незаконных инструкций, но это дает соответствующий результат. (Я думаю, что это делает задачу довольно дешевой, но если что-то разрешено, это лазейка, которую вы бы использовали ...)источник
+
. :)+
. После игры в гольф они время от+
времени выпендриваются. В конце концов, они написали достаточно программ, где им нужно было по каким-либо причинам избегать пробелов, которые+
становятся привычкой. (Он также анализирует менее двусмысленно, потому что он работает вокруг запуска особого случая в синтаксическом анализаторе для скобок.)ARM Unified Assembler Language (UAL), 3 байта
Например:
После выполнения
nop
процессор интерпретирует.ARM.attributes
раздел как код и встречает где-то недопустимую инструкцию:Проверено на Raspberry Pi 3.
источник
Microsoft C (Visual Studio 2005 и выше), 16 байт
Я не могу легко это проверить, но в соответствии с документацией он должен создать недопустимую инструкцию, намеренно пытаясь выполнить инструкцию только для ядра из программы пользовательского режима. (Обратите внимание, что из-за того, что недопустимая инструкция приводит к сбою программы, нам не нужно пытаться вернуться из
main
нее, что означает, что этаmain
функция в стиле K & R действительна. Visual Studio, никогда не переходившая из C89, обычно плохая вещь, но она пришла полезно здесь.)источник
Рубин, 13 байт
Я думаю, можно с уверенностью предположить, что мы запускаем это из оболочки * nix. Литералы backtick запускают данную команду оболочки.
$$
это запущенный процесс Ruby, а#
для интерполяции строк.Не вызывая оболочку напрямую:
Рубин, 17 байт
источник
Любая оболочка (sh, bash, csh и т. Д.), Любой POSIX (10 байт).
Тривиальный ответ, но я не видел, чтобы кто-то опубликовал его.
Просто отправляет SIGILL в текущий процесс. Пример вывода на OSX:
источник
kill -4 1
если вопрос не определен о том, какая программа выдает SIGILLYou will have root in your programs
- отбросьте байт из своего ответа и одновременно слегка троллейте вопрос: D. БОНУС: Вы убиваетеinit
init
фактически невосприимчив к сигналам, которые он специально не запрашивал, даже с root. Возможно, вы могли бы обойти это, используя другую ОС POSIX.kill -4 2
тогда: DМашинный код ELF + x86, 45 байт
Это должна быть самая маленькая исполняемая программа на Unix-машине, которая выдает SIGILL (из-за того, что Linux не распознает исполняемый файл, если он уменьшен).
Компилировать с
nasm -f bin -o a.out tiny_sigill.asm
, протестировано на виртуальной машине x64.Фактический 45-байтовый двоичный файл:
Список сборок (см. Источник ниже):
Отказ от ответственности: код из следующего руководства по написанию самой маленькой программы сборки для возврата числа, но с использованием кода операции ud2 вместо mov: http://www.muppetlabs.com/~breadbox/software/tiny/teensy.html
источник
AutoIt , 93 байта
Используя встроенную сборку flatassembler:
При запуске в интерактивном режиме SciTE он сразу вылетает. Отладчик Windows должен появиться на долю секунды. Вывод консоли будет примерно таким:
Где
-1073741795
находится неопределенный код ошибки, выдаваемый WinAPI. Это может быть любое отрицательное число.Похоже, используя мой собственный ассемблер LASM :
источник
NASM, 25 байт
Я не знаю, как это работает, просто это работает на моем компьютере (Linux x86_64).
Компилировать и запускать как:
источник
ja 0
ud2
TI-83 Hex Assembly, 2 байта
Запустить как
Asm(prgmI)
. Выполняет недопустимый код операции 0xed77. Я считаю каждую пару шестнадцатеричных цифр одним байтом.источник
Python, 32 байта
источник
import os;os.kill(os.getpid(),4)
x86 .COM, 1 байт
ARPL
вызывает#UD
в 16-битном режимеисточник
Оболочка Linux, 9 байт
Посылает
SIGILL
процессу с PID 0. Я не знаю, какой процесс имеет PID 0, но он всегда существует.Попробуйте онлайн!
источник
man kill
:0 All processes in the current process group are signaled.
GNU C,
241918 байт-4 благодаря Деннису
-1 благодаря потолку
Попробуйте онлайн! Это предполагает ASCII и x86_64. Он пытается запустить машинный код
27
, который ... является незаконным.shortC ,
1054 байтаЭквивалентен приведенному выше коду GNU C. Попробуйте онлайн!
источник
L"\6"
также незаконно, предполагая x86_64.L
это не обязательно.'
есть 39 = 0x27 , не 0x39 .MachineCode на x86_64,
21 байтПопробуйте онлайн!
Просто вызывает инструкцию x86_64
0x07
(потолочная кошка предложила 0x07 вместо 0x27)источник