1. Assembler / Говнокод #24481

    −1

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    ; минимальная .COM программа, которую я смог высрать в ascii
    ; генерит ret и передаёт на него управление
    ; пробел (and r/m8, reg) я тоже не использовал
    ; писалась в hex-кодах
    
    push 314ah
    pop ax
    xor ax, 3070h
    push ax
    pop bx
    pop ax
    push ax
    xor ah, [bx]
    sub [bx], ah
    pop ax
    push ax
    dec ax
    xor ax, 3c3ch
    xor bx, ah
    jnz 013ah

    hJ1X5p0P[XP2'('XPH5<<0'u!
    Мой малинькый скукожоный моск очинь долго мучился, прежде чем родить это. Так что, не судити строго. :)

    Запостил: 666_N33D135, 11 Июля 2018

    Комментарии (103) RSS

    • Кто сможет меньшы?
      Ответить
    • самы каротки программа DOS который я песал
      mov AH, 4Ch
      mov AL, 0
      int 21h
      Ответить
      • Самая короткая программа — retn (1 байт, только .com).
        Чуть подлиннее — int 20h (2 байта).

        Но здесь задача другая: высрать файл, который состоит только из печатаемых ASCII-символов.
        Ответить
        • а можнопросто retn рази было? я уже не помню(
          а вот 20h прерыванне вспомнил, да: звршить программу

          >>печатаемых
          еси там 1 байто то какой?
          Ответить
          • >> а можнопросто retn рази было?

            Где-то недавно я уже писал, что для com-файлов DOS кладёт в стек 16-битное слово, равное нулю. Если сделать retn, произойдёт переход на самое начало PSP. В PSP же первые два байта всегда равны CD 20 (hex), что эквивалентно инструкции int 20h.

            Про exe-файлы точно не знаю. Текущий сегмент может отличаться от того, в котором лежит PSP (если программа больше 64 килобайт и состоит из нескольких сегментов). Кладёт ли DOS в стек адрес сегмента PSP, чтобы можно было делать retf, я не знаю. Нужно проверять.
            Ответить
          • Ещё кое-что выяснил.

            По адресу PSP (ну то есть CS:[0] в случае com-файла), как я уже писал, всегда лежат байты 0CDh, 20h.

            По адресу PSP+50h лежат байты 0CDh, 21h, 0CBh — инструкции INT 21h, RETF. Ну чтобы можно было в коде программы не использовать INT 21h, а сделать CALL FAR PSP+50h. Вдруг конпелятор тупой и не умеет генерировать INT.

            По адресу PSP+05h лежит... инструкция CALL FAR на какой-то адрес (9Ah и четыре произвольных байта). Понятия не имею, кто пользуется этим полем.
            Ответить
            • Вот тута кочаим файл Help (ретроманам понравится, я гарантутую):

              http://asmworld.ru/files/

              В нём падробна описана загрузка исполняемых файлов в DOS, что и где в PSP лижит, команды 8088/8086/286, порты, прерывания и пр., что имеет отношение к DOS. В этой програмке можно узнать гораздо боьше, чем на туевой хуче саетов.
              Ответить
              • А Ralph Brown's interrupt list видел? Проект начинался как список прерываний DOS и BIOS, но потом в него вошло почти всё, что нужно. Даже недокументированные опкоды и баги процессоров есть.
                Ответить
                • Попугайчик Фотон живет в обратную сторону.
                  Скоро уже вы начнете обсуждать сборку спектрума их рассыпухи?
                  Ответить
                  • Тут кто-то ещё живёт в обратную сторону:

                    http://govnokod.ru/24405

                    Сначала эппловские языки программирования, потом статья 1981 года...
                    Ответить
                • Ну да, это даже лучше, но зато в help по-русски.
                  Ответить
    • Что нужно, чтобы exe высрать?
      Ответить
      • сигнатуру 0x5A4D

        на самом деле .exe бывает разный: может быть чистый досовый .exe, а может быть PE а моэет быть NE а может быть вообще .NET *тот же PE с mscorlib.dll)
        Ответить
        • Текстовый документ, являющийся досовским exe-файлом:
          http://www.cs.cmu.edu/~tom7/abc/paper.exe
          Пэдээфка для удобства чтения:
          http://www.cs.cmu.edu/~tom7/abc/paper.pdf
          Архив исходников:
          http://sourceforge.net/p/tom7misc/svn/HEAD/tree/trunk/abc/

          В общем, всё тут:
          http://www.cs.cmu.edu/~tom7/abc/

          Короче, PE нельзя составить только из ASCII-символов, потому что он должен содержать нули (хоть обычный, хоть .NET). NE не проверял.
          Ответить
    • Кстати, ковыряясь в попе таблице инструкций я обнаружил говно инструкцию bound (а она попадает в ascii), которая проверяет число в регистре на принадлежность диапазону, и если оно не в диаппзоне, генерит прерывание 5. Так вот, её можно использовать как jmp far (если вы достаточно упороты). };-{|}
      Ответить
      • Да, если каждый раз перед вызовом bound перезаписывать вектор 5 нужным адресом. И если ещё вспомнить, что кладёт в стек вызов прерывания (если говорить точно, то это исключение, но по факту оно работает и в реальном режиме).
        Ответить
      • Кстати, идея: скопировать в пятый вектор содержимое вектора 21h. Тогда можно будет дёргать функции DOS инструкцией bound.
        Ответить
        • Вот только интересно, почему программа bugaga зацикливается: это обработчик пятого прерывания выполняет декремент адреса, или процессор при возникновении исключения в стек кладёт уменьшенный адрес. Если второе, то мы не сможем пользоваться bound вместо int/call far. Вместо jmp far мы сможем её использовать, если нам плевать на стек и дальше ret нигде не встретится.

          Надо проверять.
          Ответить
      • >> обнаружил говно инструкцию bound

        Проверил. Сам процессор при возникновении исключения кладёт в стек в качестве адреса возврата не адрес следующей инструкции, а адрес самой инструкции, вызвавшей исключение (fault). Так что если в вектор №5 положить обычный обработчик прерывания, будет вечный цикл.

        Синтаксис yasm/nasm, за ASCII-версией не гнался:
        org 100h
        section .text
        start:
        xor ax, ax
        mov es, ax
        cli
        mov ax, [es:21h*4]
        mov [es:5*4], ax
        mov ax, [es:21h*4+2]
        mov [es:5*4+2], ax
        sti
        mov ah, 09h
        mov dx, hello
        xor di, di
        bound di, [interval]
        int 20h
        
        section .data
        interval dw 1, 2
        hello db 'Какой багор )))$'

        Чтобы выйти из цикла, нужно писать свой собственный обработчик, который перед IRET будет увеличивать адрес возврата, лежащий в стеке.
        Ответить
        • А bound же в 286 завезли? Чото я в классическом асме ее не понмю.
          Зачем вооще тащить такие высокоуровневе канструкции в асм? мож уже тогда двусторонний linked list сразу сделать?
          Ответить
          • Ральф Браун пишет, что завезли ещё в 80186/80188. Был такой редкий процессор между 8086/8088 и 80286, его аналог использовался в машинах NEC v20/v30. Но только 186 в стеке сохранялся адрес следующей инструкции (т. е. не зацикливало), а начиная с 286 стал сохраняться адрес самой инструкции bound (т. е. стало зацикливать).

            >> мож уже тогда двусторонний linked list сразу сделать?

            А MMX и SSE не смущают?
            Ответить
            • >>SSE
              а.. мм.. ну это совсем другое дело, это понятно зачем, это вручную делать долго слишком
              Ответить
              • Есть ещё странные инструкции: daa, das, aaa, aas, aam, aad.
                Ответить
                • >>aam
                  я думал это про шумоподавление у HDD]

                  >>Adjusts the result of the multiplication of two unpacked BCD values to create a pair of unpacked (base 10) BCD values.

                  поразительный проц
                  Ответить
                  • У AAM/AAD есть недокументированная (или плохо документированная) возможность: вторым байтом инструкции по умолчанию идёт 0Ah. Так вот вместо 0Ah можно использовать любое число (кроме нуля), чтобы корректировать для произвольной системы счисления, а не только для десятичной.
                    Ответить
                    • семнацатиричной можно?
                      Ответить
                      • Запросто. AAM — это всего лишь
                        AH <- AL  / base
                        AL <- AL MOD base


                        AAD — это всего лишь
                        AL <- (AH*base) + AL
                        AH <- 0

                        Возможно, при больших основаниях результат AAD будет некорректным из-за переполнения.
                        Ответить
                        • Да, именно про семнадцатеричную систему вопрос хороший. Если в AX будет лежать 1010h (AL = 10h = 16, AH = 10h = 16 — допустимые цифры для семнадцатеричной системы), то после выполнения AAD получим:
                          AL = (10h * 11h) + 10h = 120h. В байт не влезло.

                          Что будет в итоге: падение из-за переполнения или результат обрежется до 8 бит или старшие биты перейдут в AH?
                          Ответить
              • Сейчас ещё что-нибудь вспомню: XLAT, ENTER, LEAVE, BTS, BTR, BTC, BSF, BSR. XADD.
                Ответить
                • ENTER и LEAVE то чем не уголдили? лив же чистит стек и выходит
                  Ответить
                  • ENTER = push bp; mov bp, sp
                    LEAVE = mov sp, bp; pop bp

                    Сахарок.

                    Кстати, LOOP — такой же сахарок.
                    Ответить
                    • да, у меня в децтве такой макрос был
                      сахарок я понимаю как раз

                      LOOP это который JMZ по CX?
                      Ответить
                      • Заебали какую-то хуйню обсуждать. LOOk это SHGdS на GFI SH AX подходит?
                        Ответить
                        • превет как правильно напичатать число в програмированни я пока плохо умею писать но учу сиплюс помоги написатать переменую
                          Ответить
                          • Превет. Тебе нужно поставить KDE патчануть под MinGW можешь поставить также Posix GNU под прыщи
                            Ответить
                            • стабилизуется ли транзитивная гомоиконность в релиз кандидате этой версии?
                              Ответить
                              • Тебе игры программировать или что нужно?
                                Ответить
                                • хочу такую игру чтобы там срелять как в дум и чтобы гоблины как ворлд ов воркрафт как ты думаешь на сиплюс можно гоблины или надо брать джава?
                                  Ответить
                                  • А ты на каком компьютере будешь писать: на виндовсе или на пентиуме?
                                    Ответить
                                    • Ты чо, дурак, у него вообще-то Опера.
                                      Ответить
                                      • Опера это такой интернет красный, да? У меня у друга такой был, а у меня билайн
                                        Ответить
                                        • Опера — это сериал такой про хроники убойного отдела.
                                          Ответить
                                          • блядь, тогда я фраза Вистефана
                                            > у него вообще-то Опера.
                                            меня напрягает
                                            Ответить
                                    • да нет у меня ноутбук вообще
                                      компа нет
                                      Ответить
                        • >> Заебали какую-то хуйню обсуждать.

                          Смотри, что нашёл:
                          http://www.club155.ru/x86cmd/IRET

                          Тут для каждой инструкции процессора на русском написано про шлюзы задач, про уровни привилегий и про флаги защищённого режима.
                          Ответить
                          • Да много прекрссных книг

                            * Справочник программиста на персональном компьютере
                            фирмы IBM. Роберт Журден.

                            * Ирвин Кип Р. Язык Ассемблера для процессоров Intel

                            * М Гук. Аппаратные средства IBM PC

                            * Tom Shanley (MindShare): ISA System Architecture и x86 Instruction Set Architecture
                            Ответить
                      • LOOP ... — это DEC CX; JCXZ ...

                        Удобно, конечно, что в одну инструкцию вместо двух. Но на каких-то пнях он почему-то стал медленнее, чем две инструкции, поэтому его перестали использовать.
                        Ответить
                        • s/JCXZ/JNCXZ/
                          Ответить
                          • > JNCXZ
                            Нет такого опкода, JCXZ,JECXZ есть.

                            LOOP offfset8 - можно заменить на dec [e]cx; jnz offset8, но LOOP не меняет флагов.
                            Ответить
                        • что то писал про это Фог, но я уже не помню конечно. Но что-то такое было.

                          В 16ти битном асемблере ее очень часто юзали, хотя опять таки были какие-то макросы которые изображали даже целый FOR
                          Ответить
                          • Котаны, раз уж пошла такая ностальгия, подскажиtе, чем наши предки искали файлы в win 3.1? FindFirstFile нету (
                            Ответить
                            • Не поверишь... Windows 3.x делала обёртки защищённого режима для прерывания DOS, поэтому в программах той эпохи можно было встретить не только импорт из dll, но и инструкцию int 21h.
                              Ответить
              • Ещё одно заклинание: cmpxchg. Надеюсь, никого не вызвал.
                Ответить
                • а вот это как раз важное заклинание
                  потому что как иначе атомарно что-то сделать когда у тебя особенно 8 ядер? через lock вручную?
                  Ответить
          • А вообще интересно, о чём думал тот, кто в 286 решил из обработчика аварийной ситуации bound сделать возврат на саму инструкцию.

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

            Можно, конечно, сделать тупо, чтобы обработчик загонял проверяемое значение в указанные рамки (есть же в MMX инструкции, которые усекают результат), но нет уверенности, что с такой семантикой согласятся все.
            Ответить
            • Обработчик должен выдать ошибку пользователю)

              Вспомни как работает Fault в protected. Он возвращает управление на шаг ДО бедовой инструкции (в отличие от Abort и Trap). Думаю что и тут пытались как-то так же

              ps:
              а собссно чот я нашел

              Bound Range Exceeded 5 (0x5) Fault

              спасибо тебе .osdev, наш interrupt list современности
              Ответить
              • Ну то есть в нормальном окружении он должен свалить программу и записать куда-нибудь отладочную информацию?
                Ответить
          • А вообще интересно, о чём думал тот, кто в 286 решил из обработчика аварийной ситуации bound сделать возврат на саму инструкцию.

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

            Можно, конечно, сделать тупо, чтобы обработчик загонял проверяемое значение в указанные рамки (есть же в MMX инструкции, которые усекают результат), но нет уверенности, что с такой семантикой согласятся все.
            Ответить
        • Результат работы программы (после останова по Ctrl+Break):
          https://i.imgur.com/wMz2E2g.png
          Ответить
        • Победил зацикливание:
          org 100h
          section .text
          start:
          xor ax, ax
          mov es, ax
          cli
          mov ax, handler
          mov [es:5*4], ax
          mov ax, cs
          mov [es:5*4+2], ax
          sti
          mov ah, 09h
          mov dx, hello
          xor di, di
          bound di, [interval]
          int 20h
          
          handler:
          push bp
          mov bp, sp
          add word[bp+2], 4 ; Achtung! Сработает, только если за bound следует 16-битное immediate
                            ; В общем случае нужно проверять mod R/M у вызывающей инструкции
          pop bp
          int 21h
          iret
          
          section .data
          interval dw 1, 2
          hello db 'Какой багор )))$'

          Сделал trap из fault, проверь. Теперь bound можно использовать для вызова подпрограмм.
          Ответить
        • С 5-м прерыванием вообще смешная история: intel считали его зарезервированным, а в биосе его заюзали как обработчик PrintScreen.
          Ответить
          • Дебаг: если программа свалилась из-за выхода индекса за границы диапазона, нужно сделать твёрдую копию содержимого экрана.
            Ответить
    • ;100
              push 33h                    
      ;102
              pop ax
      ;103
              cmp al, 3ah    ; CF = 1, т. к. 33h < 3ah; AF = 1, т. к. 3 < a
      ;105
              daa            ; ax = 99h (прибавили 6, т. к. был AF=1; прибавили 60h, т. к. был CF=1)
      ;106
              push ax                     
      ;107
              pop bx         ; bx = 99h
      ;108
              xor [bx+0x72], bl      ; [99h + 72h = 10bh]; 5Ah xor 99h = 0C3h
      ;10b
              pop dx                 ; db 5Ah


      Итого:
      j3X<:'P[0_rZ

      12 байт
      Ответить
      • специальная олимпиада, о достойнейший?
        Ответить
        • Так точно!

          Сможешь короче, о джинн?
          Ответить
          • Не могу, мне надо писать про хуи и анусы
            Ответить
            • Но кто тебя держит в лампе и заставляет писать про хуи и анусы?
              Ответить
              • >>>Но кто тебя держит в лампе

                Я давно уже не пользуюсь "LAMP" а только "LNMP": я использую "nginx" и "php-fpm"
                Ответить
      • Крис, это ты? Ты же умер:(((
        Ответить
        • Нет. Крис наверняка был бы в ужасе от моей программы. Я же изменяю следующий байт после выполняемой инструкции. Наверняка этот байт уже попал в конвейер и в предиктор, а из-за меня процессору приходится сбрасывать конвейер и декодировать этот байт заново.
          Ответить
          • не просто попал, а может быть даже уже выполнился спекулятивно (вспоминаем про мельдоний и его друзей)

            но у тебя же нет задачи сделать быстро
            Ответить
      • Следующий шаг:
        ;100
                push 21h                    
        ;102
                pop bx
        ;103
                xor bx, [bx+31h]      ; bx = 21h xor 0CBh = 0eah
        ;106
                dec bx                ; bx = 0e9h
        ;107
                xor [bx+21h], bl      ; [0e9h + 21h = 10ah]; 2ah xor e9h = 0C3h
        ;10a
                db '*'                ; db 2ah


        11 байт:
        j![3_1K0_!*
        Ответить
        • Бля, ты риально дух Касперски. Ты никодимпобедим.
          Ответить
          • ;100
                    push 22h                    
            ;102
                    pop bx
            ;103
                    xor bx, [bx+30h]      ; bx = 22h xor 0CBh = 0e9h
            ;106
                    xor [bx+20h], bl      ; [0e9h + 20h = 109h]; 2ah xor e9h = 0C3h
            ;109
                    db '*'                ; db 2ah

            10 байт, пришлось использовать пробел:
            j"[3_00_ *
            Ответить
            • Бля, ты шустрой! Зото у миня без пробела:
              j#[3_/0_!+

              то же самое, только поколдовал с константами у убрал декремент:
              push 23
              pop bx
              xor bx, [bx+2f]
              xor [bx+22], bl
              db 2b
              Ответить
              • j#[2_/0_!+

                Пофиксил, потому что хз, всегда ли по адресу PSP:0053 лежит 0.
                push 23
                pop bx
                xor bl, [bx+2f]
                xor [bx+22], bl
                db 2b


                ЗЫ. Пробовал избавиться от смещений, пробовал генерить не ret, а int 20h, но никак не получается меньше.
                Ответить
            • > 10 байт

              походу теперь можно забрутить все кобенации :D
              Ответить
              • Печатаемых ASCII-символов 128-32-1 = 95.
                95 в 10-й степени — это примерно 6 × 10¹⁹. При скорости перебора миллион кобенаций в секунду брутить будешь 17 миллиардов часов.

                Тут нужно генерировать кобенации хитро.
                Ответить
                • Если сразу проверять составляемый код -- не так уж много вариантов выживет, как мне кажется. Большая часть поддеревьев, у которых в начале всякие [bx+foo] будет отброшена сразу.
                  Ответить
                  • Пробрутить хотя бы все ascii-программы длины из отрезка [2; 5]
                    Если найдётся хотя бы одна удовлетворяющая, будет победа. Не знаю, насколько реальная цифра проверять миллион кобенаций в секунду, но в таком случае займёт 3 часа.
                    Ответить
                • Пробел считается печатаемым? Если нет тогда 94 аски символа.
                  Ответить
        • Можно создать пустой файл (под досбоксом работает)
          Ответить
    • Кстати, когда загружаешь программу досовским debug'ом в регистре AX всегда FFFF. Это всегда так, или это делает сам debug?
      Ответить
      • Нет. Может быть ноль, могут быть и другие значения. Это просто так повезло.

        Полагаться можно только на значение IP (в com-файлах всегда 100h), на то, что WORD PTR [SP] = 0, а также на то, что CS=DS=ES=SS (в com-файлах; в экзешнике стек и данные могут быть разведены по другим сегментам).
        Ответить
        • цiкаво, какие гарантии даются первому пользовательскому коду (MBR например)?
          AFAIK CPU после старта (ну когда PWR_OK устаканица) гарантирует только верное значение IP (он указывает на POST в бивис) ну и, само собой, реальный режим. А когда биос передает управление MBRу -- там что гарантируется?

          ps: невзапно:
          https://www.freebsd.org/doc/en/books/arch-handbook/boot-boot0.html

          >>Because boot0 is loaded by the BIOS to address 0x7C00, it copies itself to address 0x600 and then transfers control there

          ох
          Ответить
          • Следующей задачей мы будем писать MBR в ASCII-кодах?

            CS:IP = 0: 7C00h

            В регистре DL лежит номер текущего диска (80h для первого жёсткого, например), чтобы MBR смогла прочитать остальные сектора, вызвав INT 13h.

            В последних двух байтах MBR лежит сигнатура 55h, AAh.

            ES:DI может указывать на сигнатуру Plag-n-Play.

            Больше ни на что полагаться нельзя. Разные BIOS могут заполнять остальные регистры по-разному.
            Ответить
            • >>В последних двух байтах MBR
              ну то не состояние процессора же

              >>может
              ну знач ен можем

              >> Plug-n-Pray.
              Можа потом сканировать память в поисках 55h, AAh. чтобы найти ISA PnP, да.
              Или можно расчитывать что это сделал бивис (если PnP OS Installed = false) и заполнил какие-то говнотаблицы которые тоже уже наверняка давно никто не заполняет.

              зы: а ты понял зачем бздуны скопировали свой загрузчик с 0x7C00 в 0x600 и jmp туда?
              Ответить
              • Загрузчик копируют не только бздуны.

                DOS тоже копирует BR раздела в 0x600 и jmp туда, потому что ему нужен непрерывный массив памяти в адресах до 0xA000:0x0000 = 0xA0000 (640 КБ), а 0x7C00 ни высоко, ни низко: съест примерно 32 килобайта от начала, что для DOS непозволительная роскошь.
                Ответить
                • зачем же дизайнеры PC его в такое странное место сунули? Плевок-в-вечность?
                  Ответить
      • Оказывается, по адресу PSP+81h всегда лежит пробел.

        PSP+80h — это строка (в паскалевском формате, первый байт — длина), в которой лежат параметры командной строки (не включая имя самой программы). Параметры отделяются от имени самой программы пробелами, поэтому эта строка всегда начинается с пробела. А завершается эта строка байтом 0Dh ("\r", возврат каретки).

        Ещё DOS в PSP заполнят два FCB (по адресам PSP+5Ch и PSP+6Ch). В качестве имён файлов берёт первый и второй аргумент командной строки соответственно (если аргументов не было, фигачит пробелы), остальные поля FCB заполняет нулями.

        Остальные поля PSP либо заполняются труднопредсказуемыми значениями, либо не документированы и во всяких досбоксах могут быть произвольными.
        Ответить
        • >>в паскалевском формате
          >> А завершается эта строка байтом 0Dh ("\r", возврат каретки).
          запахло классической макосью
          Ответить
    • Что должна делать программа? И какой критерий её валидности?
      Ответить
      • Парограма COM только в аски кодах, ничего ни делает, просто завершается. Но гуест нипабидим.
        Ответить
        • Вот и нихуя. Ни код гуеста, ни твой, у меня не работают. Windows 7.
          Ответить
          • 64-битная или 32-битная? Из 64-битной выпилили ntvdm, на неё нужно ставить эмулятор (Dosbox или ещё какой-нибудь).
            Ответить
            • выпилилипилилили бо x64 не може в VM86.
              Ответить
              • x64 не может в VM86 в длииииинном режиме. А переключать в короткий ради запуска одной программы его никто не будет.

                Кстати, чем грозит переключение режима?
                Ответить
                • всмысле писнуть в FLAGS[17]=1?

                  Fault поди получишь или что-то такое
                  Ответить
            • Всё понятно, наебалово.
              Так и надо было писать в условии задачи.

              Мелким шрифтом хотябы
              Ответить
    • JSON
      Ответить

    Добавить комментарий