LINUX.ORG.RU

Объявлены победители 29 конкурса по написанию запутанного кода на языке Си

 , ,


0

4

Опубликованы исходные тексты работ, победивших в двадцать девятом конкурсе IOCCC (International Obfuscated C Code Contest), участникам которого предлагалось подготовить наиболее запутанный и трудноразбираемый код на языке Си. Участвующие в конкурсе работы, с одной стороны, должны препятствовать анализу кода и пониманию сути решаемой задачи, но, с другой стороны, код должен быть интересен и чем-то примечателен (работы могут быть необычно оформлены или выделять неожиданные стороны языка Си). Размер файла с кодом программы не должен превышать 4993 байтa, а чистый код не должен превышать 2503 байта после обработки утилитой iocccsize.

Среди победителей:

  • Эмулятор компьютера с архитектурой URISC, набор команд в котором ограничивается одной инструкцией SUBLEQ (SUbtract and Branch if Less than or EQual to zero). Размер эмулятора всего 366 байт, при том, что помимо CPU он эмулирует фреймбуфер с разрешением 800x512, используя для вывода графики библиотеку SDL3, и может загрузить образ с Linux и запустить в нём игру doom.
  • Генератор изображения чёрной дыры. Приложение включает простой интерпретатор для подмножества языка Fortran 66, программа для которого задана в форме перфокарт, закодированных через пробелы и табуляции в исходном коде. Закодированная Fortran-программа повторяет первый код для симуляции чёрной дыры, опубликованный Жан-Пьером Люмине в 1978 году. Изображение формируется в виде облака точек и сохраняется в формате PGM. Кроме симуляции чёрной дыры предложены варианты с закодированными «перфокартами» для расчёта множества Мандельброта, вычисления простых чисел и трассировки лучей.
  • Вариант утилиты patch, генерирующий утилиту diff через серию трансформаций собственного кода. На первом этапе скомпилированной утилите patch передаётся собственный исходных код, на основе которого формируется diff-файл. После применения этого diff-а к собственному коду на выходе получается программа, которая в цикле на основе своего кода генерирует набор коммитов с патчами в формате git am. При объединении данных коммитов командой git log --pretty=format:%s > final.c получается код с реализацией утилиты diff.
  • Игра (на скриншоте) в жанре Roguelike, работающая в текстовом терминале и позволяющая проходить автоматически генерируемый лабиринт, собирать артефакты и избегать монстров. Код оформлен в виде изображения подземного жителя и обфусцирован (строки зашифрованы, циклы реализованы через goto, при работе с массивами используется синтаксис «индекс[массив]»).
  • Генератор ASCII-анимации, воссоздающий заставку сериала «Доктор Кто» с симуляцией видеоэффекта «HowlRound» (туннель из уменьшающихся копий изображения), применявшегося в заставке 1963 года.
  • Эмулятор игровой приставки GameBoy, оптимизированный для запуска тетриса, но способный выполнять и другие игры (протестирован запуск ROM-файлов для десятка игр). Вывод формируется в форме псевдографики из Unicode-символов.
  • Симулятор звука морского прибоя на фоне автоматически генерируемой медитативной музыки. На выходе генерируется wav-файл, продолжительностью 5 минут.
  • Реализация самомодифицирующегося игрового автомата Quine Pong, предоставляющего две игры - пинг-понг и перепрыгивающий препятствия динозавр (как в пасхальном яйце из Google Chrome). Программа примечательна тем, что отображение кадров реализовано через цикличную перегенерацию кода программы (запуск приводит к выводу исходного кода для первого кадра, после компиляции этого кода формируется код для следующего кадра и так далее). Игровой процесс реализован через shell-скрипт, выполняющий цикличную перекомпиляцию кода.
  • Компилятор и генератор кода для языка Zoltraak. Язык включает только одно слово «zoltraak», которое комбинируется в разной форме с пробелами и пустыми строками. На вход подаётся любой текстовый файл, который преобразуется в программу на языке Си, состоящую из заголовка и последовательности на языке Zoltraak. Компиляция и выполнение сгенерированной Си-программы приводит к выводу содержимого исходного текстового файла.

Видео на youtube, длительность: 2:57:20.

>>> Источник: OpenNET

★★★★★

Проверено: maxcom ()
Последнее исправление: dataman (всего исправлений: 3)
Ответ на: комментарий от madcore

Проблемы с очередью команд дофига чего вызывает. И это исключительно проблемы конструкции этой очереди, между прочим.

Stanson ★★★★★
()
Ответ на: комментарий от Stanson

ога, а ещё они провинились тем, что чисто фон-неймовских сейчас а природе не существует, все как-то больше гарвардские при вскрытии
почему бы так?

madcore ★★★★★
()
Ответ на: комментарий от madcore

Реальных гарвардских мало, и в основном это однокристалки.

То, что конвейер и кеш разные сущности, не делает из фон-неймановской гарвардскую на самом деле.

Но можно наковырять ОС, которая будет делать страницы с кодом readonly, а страницы с данными noexec и позволять загружать только подписанные бинарники, но это будет костылегарвард только для userspace, и только для процессоров которые в noexec умеют.

Stanson ★★★★★
()
Ответ на: комментарий от Stanson

Нет вообще никакой разницы, насколько вычурные конструкции используются в ЯП. Всё равно они предназначены исключительно для создания чисто императивного кода для CPU, который и будет на самом деле выполняться.

Если бы не было разницы, все писали бы на ассемблере. И хранили бы исключительно целые числа от 0 до 255. Ведь как ни трактуй данные, в памяти они последовательность байтов.

Программа на любом языке используют исключительно возможности процессора фон-неймановской архитектуры по совершенно очевидным причинам.

Бывают языки программирования, для которых невозможно написать компилятор (например, PicoLisp).

Упомянутый выше Prolog позволяет писать недетерминированные программы.

Желание вывести свойства языка программирования из архитектуры процессора аналогично желанию вывести химические свойства веществ из физических свойств атомов, а поведение живых существ свести к химическим взаимодействиям.

monk ★★★★★
()
Ответ на: комментарий от shdown

Так ghc ещё не так давно в C компилировал. И сейчас может, хотя теперь нужно его специально компелять для этого.

Я не про то, что невозможно в Си преобразовать. Я про то, что при программировании на Си невозможно достаточно лаконично описать что-то вроде factorials = 1 : zipWith (*) factorials [1..]. И при преобразовании в Си получается очень тормозной код.

<ucontext.h> из POSIX.

И как с его помощью получить аналог

int f() 
{
  yield 1;
  yield g();
  yield 3;
}
?
monk ★★★★★
()
Ответ на: комментарий от Stanson

Программа на любом языке используют исключительно возможности процессора фон-неймановской архитектуры

Уже нет: https://habr.com/ru/articles/505360/

monk ★★★★★
()
Ответ на: комментарий от monk

вывести химические свойства веществ из физических свойств атомов

А что не так? Я думаю всю химию можно заменить чисто описанием физических процессов, при достаточной вычислительной мощности. Целесообразность, конечно, под вопросом, но всё же.

ptah_alexs ★★★★★
()
Ответ на: комментарий от Stanson

Мне кажется что на гарвардскую архитектуру намекают раздельные кэши L1 для инструкций и данных, которые есть во всех процессорах сейчас.

ptah_alexs ★★★★★
()
Ответ на: комментарий от ptah_alexs

Я думаю всю химию можно заменить чисто описанием физических процессов, при достаточной вычислительной мощности.

Проблема в терминах. Химические реакции не описываются в терминах координат и скоростей частиц и даже волновых функций.

У Шекли есть хорошая иллюстрация: https://litmir.club/br/?b=83905

monk ★★★★★
()
Ответ на: комментарий от Stanson

более того на явление что нотация человеков отличается от нотации исполнителя(асм как текстовое представление 1в1 машкода - оставляем в стороне макросы асмы и «оптимизирующие"асм_трансляторы) влияет что есть ещё „хранилище“

и зависит от коньюктуры хранят текстовый образ или бинарь готовый к исполнению (отложенная линковка и прочих адд-dll попытка усидеть на всех стульях разом без фатальных последствий)

да даже появление полей регистров в отличии от полей адрессов(которые в первую очередь были повсеместной причиной самомодификации когда нет ни косвенности ни флагов поэтому буквально вычисление и перезапись адресса в безусловном джампе единственный способ сделать ветвление — которое ещё и если ветвь выхода сверх редкая то буквально может быть быстрее чем переход по условию

т.е. реальный вопрос насколько код интерпретируемый(а проверка динамического условия это микро рефлексия) или прямой как шпала ( который будет реально сверх быстрым на линейных участках )

т.е. можно представить компилятор того же Си который будет выдавать код с минимум команд ветвления(ну профилировать нуна) что бы выход из цикла осуществлялся только тогда когда вычисления перетирают адрес куда „continue“ - и сама проверка не на каждой итерации а только по срабатывании кучи тригеров условий

:)

основная имхо причина отказа под самомодифицирующихся образов - то что есть язык реализации и язык исполнения и более того апаратная реализация стека и регистров уменьшило необходимость в прикладном коде переписывать поля команд

qulinxao3 ★☆
()
Ответ на: комментарий от ptah_alexs

Они никак не препятствуют фон-неймановскости в смысле выполнения кода и набора инструкций.

Stanson ★★★★★
()
Ответ на: комментарий от monk

Бывают языки программирования, для которых невозможно написать компилятор (например, PicoLisp).

И работают они на сферическом коне в вакууме.

Упомянутый выше Prolog позволяет писать недетерминированные программы.

Недетерминированные они исключительно для «программиста на Прологе». :)

Желание вывести свойства языка программирования из архитектуры процессора аналогично желанию вывести химические свойства веществ из физических свойств атомов,

Вообще-то химические свойства веществ именно так и выводятся со времён Менделеева. :)

а поведение живых существ свести к химическим взаимодействиям.

Ну 99% биомассы именно так и работает. Даже люди такие попадаются. :)

Stanson ★★★★★
()
Ответ на: комментарий от qulinxao3

Человеки, в отличии от, способны создавать новые причинно-следственные цепочки. Может быть даже не только человеки. Так что никакого редукционизма.

Stanson ★★★★★
()
Ответ на: комментарий от Stanson

И работают они на сферическом коне в вакууме.

На интерпретаторе. Но их команды не соответствуют однозначно командам процессора.

Недетерминированные они исключительно для «программиста на Прологе». :)

Заяндекси недетерминированный конечный автомат.

Вообще-то химические свойства веществ именно так и выводятся со времён Менделеева. :)

Выведи свойство воды иметь в твёрдом состоянии меньшую плотность, чем в жидком, из свойств водорода и кислорода.

Ну 99% биомассы именно так и работает. Даже люди такие попадаются. :)

Смешно. В химии, например, нет понятия «обучение». И описание его через химические реакции неимоверно сложно.

monk ★★★★★
()
Ответ на: комментарий от Stanson

Человеки, в отличии от, способны создавать новые причинно-следственные цепочки.

Ты же сообщением выше писал, что человека можно полностью химическими реакциями описать. Это и есть редукционизм.

monk ★★★★★
()
Ответ на: комментарий от monk

И при преобразовании в Си получается очень тормозной код.

Это ещё почему?

И как с его помощью получить аналог

Можно. Но не совсем «лаконично». Ну, по крайней мере много boilerplate нужно будет до самой реализации. Но это не так важно.

shdown ★★
()
Ответ на: комментарий от monk

Ты же сообщением выше писал, что человека можно полностью химическими реакциями описать

Я писал что некоторых человеков ознозначно можно.

Stanson ★★★★★
()
Ответ на: комментарий от monk

На интерпретаторе.

Который детерминирован.

Заяндекси недетерминированный конечный автомат.

Ты не можешь сделать ничего реально недетерминированного на основе чего-либо 100% детерменированного.

Всяческие ментальные упражнения могут сделать чёрное белым только в голове упражняющегося.

Stanson ★★★★★
()
Ответ на: комментарий от Stanson

Дребезг контактов?

Какое не было распределение(кроме мощности множеста возможных значений меньше чёта)

По опре гауса колокола Мона получить колокол , дальше гуляй губерния

qulinxao3 ★☆
()
Последнее исправление: qulinxao3 (всего исправлений: 1)
Ответ на: комментарий от shdown

Это ещё почему?

Потому что семантика не совпадает и приходится делать много лишних действий. Почитай, почему отказались от этого транслятора.

Можно. Но не совсем «лаконично».

Понятно, что в пределе на любом полном по Тьюрингу языке можно сделать интерпретатор Haskell.

Речь про то, что ленивые структуры в программах на Си практически не используются.

Кстати, если брать классический UNIX, он основывается как минимум на трёх принципиально разных языках: C с семантикой близкой к процессору, shell с семантикой конвейера команд, awk с семантикой конечных автоматов (регулярные выражения и условия на строки).

И знание C мало помогало выучить последние два, так как идиоматические конструкции очень отличаются.

monk ★★★★★
()
Ответ на: комментарий от Stanson

Который детерминирован.

И что? Недетерминированный конечный автомат реализуется детерминированным алгоритмом. Но среди команд процессора недетерминированных (выполняющих несколько веток) нет.

monk ★★★★★
()
Ответ на: комментарий от Stanson

Забавно как играя тут мы или а тут мы и можно элизить диспут

Ты утверждал что Picolisp комлириуем в эквивалентную по поведению интерпретатора форму в логической цепочке абзацев (которые у читателя связываются через и а у тебя тут через или) ты помянул не детерминированность невозможна в ЯПах.

Те скучно!?

qulinxao3 ★☆
()
Ответ на: комментарий от qulinxao3

Дребезг контактов?

Современные компьютеры?

Stanson ★★★★★
()
Ответ на: комментарий от monk

Недетерминированный конечный автомат реализуется детерминированным алгоритмом.

Нет, не реализуется. Написание теоретических статей и пр. не является реализацией чего-либо.

Как в параллельном топике - show me your code.

Stanson ★★★★★
()
Последнее исправление: Stanson (всего исправлений: 1)
Ответ на: комментарий от Stanson

Нет, не реализуется. Написание теоретических статей и пр. не является реализацией чего-либо.

Как в параллельном топике - show me your code.

Легко.

states([q0, q1, q2]).
symbols([a, b]).
transition(q0, a, q1).
transition(q0, a, q2).
transition(q0, b, q2).
transition(q1, a, q2).
transition(q1, b, q0).
transition(q1, b, q2).
transition(q2, a, q1).
transition(q2, b, q2).
startState(q0).
finalStates([q2]).

Это недетерминированный конечный автомат. Для состояния q0 и символа a, например, два перехода.

Пролог выполняет этот код на детерминированном процессоре, в котором каждая ассемблерная инструкция позволяет только один переход из заданного состояния.

monk ★★★★★
()
Ответ на: комментарий от monk

Для состояния q0 и символа a, например, два перехода.

И каким же конкретно непредсказуемым образом выбирается только один из них? :)

Или это просто банальнейший генератор всех возможных вариантов развития событий которые удовлетворяют описанным в transition правилам?

Stanson ★★★★★
()
Ответ на: комментарий от Stanson

И каким же конкретно непредсказуемым образом выбирается только один из них? :)

Не выбираются. Выполняются всегда все ветви.

Или это просто банальнейший генератор всех возможных вариантов развития событий

Генератор и анализатор. Можно получить все варианты состояний для данной строки. Можно получить все строки для данного начального и конечного состояния. Можно проверить, соответствует ли слово данному автомату (для произвольного начального состояния).

банальнейший генератор

Я не пойму. Вы не только языки программирования кроме Си, но и теорию автоматов игнорируете?

monk ★★★★★
()
Последнее исправление: monk (всего исправлений: 3)
Ответ на: комментарий от monk

Не выбираются. Выполняются всегда все ветви.

Ну и в чём же здесь недетерминированность?

Практическая задача одной из моих софтин, например, именно в этом и заключается - найти все возможные комбинации из элементов списка, сочетание которых удовлетворит определённому, заранее заданному списку условий. Вот прям буквально. Какой набор пигментов и в каком соотнношении надо смешать, чтобы получить тот или иной цвет с теми или иными характеристиками. Иногда это генерирует сотни разных рецептов. Вообще никакой недетерминированности в этом нет и быть не может от слова совсем. Наоборот, всё детерминировано до безобразия. :)

И нет, никакие прологи и прочие хацкели не способны решать такую задачу проще и быстрее чем сишечка. И даже никакой рекурсии тут нахер не нужно - перебор всех возможных уникальных комбинаций до n элементом из m, внезапно, сводится к единственному циклу for, на каждом проходе которого вычисляется и обрабатывается очередная и уникальная новая комбинация. :)

Я не пойму. Вы не только языки программирования кроме Си, но и теорию автоматов игнорируете?

Я игнорирую теории о сферических конях в вакууме, которые заведомо не подразумевают какого-либо реального практического воплощения. Можно бесконечно рассуждать о машине работающей с бесконечной лентой с командами, но никакой практической пользы от этого не появится. Ну кроме тех или иных личных профитов для рассужающих, разумеется.

Stanson ★★★★★
()
Последнее исправление: Stanson (всего исправлений: 1)
Ответ на: комментарий от Stanson

И нет, никакие прологи и прочие хацкели не способны решать такую задачу проще и быстрее чем сишечка. И даже никакой рекурсии тут нахер не нужно - перебор всех возможных уникальных комбинаций до n элементом из m, внезапно, сводится к единственному циклу for

Интересно, а SQL тоже игнорируете? Ведь цикл for успешно заменяет любой запрос.

monk ★★★★★
()
Ответ на: комментарий от monk

декларативное описание всё равно ведь разворачивается в эти самые циклы и карты переходов

madcore ★★★★★
()
Ответ на: комментарий от monk

Интересно, а SQL тоже игнорируете?

SQL - такой себе ЯП. Я его рассматриваю скорее как относительно стандартный интерфейс к БД с плюшками, а не как средство для написания программ. Ну можно там триггер какой наковырять, или процедурку по-мелочи, если приспичит, но чтобы прям программировать на SQL - нафиг-нафиг.

Stanson ★★★★★
()
Последнее исправление: Stanson (всего исправлений: 1)
Ответ на: комментарий от Stanson

Ты не можешь сделать ничего реально недетерминированного на основе чего-либо 100% детерменированного.

А что есть детерминированного в современных процессорах? rdtsc, rdrand, троттлинг, branch prediction, auto-prefetch. Там ещё какую-то дичь завезли, что не теперь гарантируется, что mov, add, mul, etc будут constant-time.

shdown ★★
()
Последнее исправление: shdown (всего исправлений: 1)
Ответ на: комментарий от shdown

А что есть детерминированного в современных процессорах?

ISA.

  • rtdsc - 100% детерминирован - это же часы, их основная фича и есть максимальная детерминированность.
  • rdrand тоже вполне детерминирован - команда всегда выполняет только операцию для которой создана, да и даже результат этой команды неспроста вызвал шитшторм на предмет использования в качестве единственного источника /dev/random
  • тротлинг и прочая, вместе с IntelME и аналогичной парашей на других процессорах не имеют отношения к детерминированности выполнения инструкций. А вот реалтайму несомненно гадят по полной, если вдруг он понадобился.

Нет инструкций, которые могут делать что-то непредсказуемое.

Единственный способ достичь псевдонедетерминированности, т.е. когда нет практически применимого в реальности способа не то что предсказать, а даже предположить, каков будет результат выполнения програмы и какой код будет выполняться, это самомодифицирующийся код. Да и то это лишь псевдонедетерминированность, просто потому что за разумное время будет невозможно (или крайне затратно) раскрутить, что же программа на самом деле делает.

И собственно эта псевдонедетерминированность это лишь возможный сторонний эффект применения доступной в фон-неймановской архитектуре но единственной неиспользуемой ни в одном языке программирования по полной программе фичи самомодификации кода, которая единственная может привести к необходимости учить что-то реально новое в программировании для человека который умеет программировать.

Stanson ★★★★★
()
Последнее исправление: Stanson (всего исправлений: 2)
Ответ на: комментарий от Stanson

Нет инструкций, которые могут делать что-то непредсказуемое.

Мне рассказывали, что если исполнить определённую последовательность байт, которая формально #UD, активируется секретный режим, в котором тайминги любой операции предсказуемы: отключается branch prediction, кэши и всё остальное. Но это только «кому надо» сообщают. Но ты, по твоим словам, работал там, тебе виднее.

rtdsc - 100% детерминирован

И что вернёт эта функция?

; unsigned nd(void);
; SYSV ABI

.text
.global nd
nd:
    rdtsc
    mov %eax, %ecx

    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax
    add $1, %eax

    rdtsc
    sub %ecx, %eax

    ret

(Можешь добавить выравнивание и сделать цикл, который помещается в 16 байт, чтобы кэш не играл роли. Не суть.)

shdown ★★
()
Последнее исправление: shdown (всего исправлений: 3)
Ответ на: комментарий от shdown

Мне рассказывали, что если исполнить определённую последовательность байт, которая формально #UD, активируется секретный режим

То, что кто-то не знает что есть какой-то секретный режим и как в него попасть, совершенно не означает что этот режим недетерминирован, или что ISA процессора недетерминирована.

И что вернёт эта функция?

Очевидно, вернёт время выполнения 50 операций сложения с константой. И всегда будет возвращать именно его. Она не начнёт внезапно вычислять синус, например, и не станет заполнять кусок памяти картинкой с котиком. Просто всегда будет возвращать время выполнения 50 операций сложения. Делать ровно то, что ты и написал. Прикинь?

Stanson ★★★★★
()
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.