Правим баг без исходных кодов

    image

    В предыдущей статье мы разобрали, как реверс-инжиниринг может помочь в получении каких-либо преимуществ перед остальными пользователями. Сегодня мы поговорим ещё об одном применении обратной разработки — исправлении багов в отсутствии исходных кодов приложения. Причин заниматься подобными вещами может быть целое море — разработка программы давным-давно заброшена, а её сорцы автор так и не предоставил общественности / разработка ведётся совершенно в другом русле, и авторам нет никакого дела до возникшего у вас бага / etc, но их объединяет общая цель — исправить сломанный функционал, который постоянно вам досаждает.

    Что ж, ближе к делу. Есть такая широко известная в узких кругах программа под названием «Govorilka». Как объясняет её автор, это ничто иное, как «программа для чтения текстов голосом». По сути, так оно и есть. При помощи неё было озвучено множество популярных и не очень видео, рапространившихся по всей сети. Программа имеет консольную версию под названием «Govorilka_cp», которую удобно вызывать из своих собственных приложений, что, собственно, я и сделал в одном из своих проектов.

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

    Учитывая, что говорилка не обновлялась уже несколько лет, а сам автор оставил вот такое «послание» на своём сайте

    image

    , я понял, что надеяться мне не на кого, и решать проблему придётся самому.

    Как протекал процесс, и что из этого вышло, читайте под катом (осторожно, много скриншотов).

    Прежде чем загружать говорилку в OllyDbg, давайте посмотрим, не защищена ли она каким-нибудь протектором. Берём в руки DiE и видим следующую картину:

    image

    Судя по его выводу, программа запакована ASPack'ом. Для убедительности воспользуемся ещё одним анализатором — PEiD:

    image

    Надеясь, что два анализатора не могут ошибаться одновременно, приходим к выводу, что говорилка действительно накрыта ASPack'ом. Ну, ничего, давайте его снимем.

    Избавление исполняемого файла от ASPack'а можно разделить на три основных этапа:
    • Поиск OEP (Original Entry Point — адрес, с которого бы начинала выполняться программа, если бы не была упакована)
    • Снятие дампа
    • Восстановление IAT

    Начнём, разумеется, с поиска OEP. Один из самых простых способов обнаружить оригинальную точку входа — поставить хардварный бряк на ESP-4, потому что большинство паковщиков после своей работы восстанавливают стек. Запускаем говорилку в OllyDbg, открываем Command Line при помощи Alt-F1 (окно стандартного плагина, который поставляется вместе с самим отладчиком), вводим команду hr esp-4, нажимаем F9 и останавливаемся вот на таком месте:

    image

    Теперь нам надо пробежаться до ближайшего return'а при помощи Ctrl-F9, нажать F8 и… оказаться на OEP, которая в данном случае находится по адресу 0x0045A210:

    image

    Пришло время снимать дамп. Скачиваем и устанавливаем плагин для OllyDbg под названием OllyDump, перезапускаем отладку при помощи Ctrl-F2, снова останавливаемся на OEP и выбираем пункт меню «Dump debugged process», который находится в Plugins -> OllyDump:

    image

    Нажимаем кнопку «Dump», выбираем имя выходного исполняемого файла и… получаем вот это при попытке его запустить:

    image

    Значит, что-то не так с импортами. Ничего, восстановим их вручную.

    Дампим процесс говорилки ещё раз, но на этот раз снимаем галочку с CheckBox'а «Rebuild Import». Скачиваем ImpREC, выбираем процесс говорилки из списка, вводим адрес 5A210 в поле для OEP — 0x0045A210 — image base (0x400000) = 0x5A210 и нажимаем на кнопку «IAT AutoSearch»:

    image

    Делаем, как сказали, и наблюдаем следующую картину:

    image

    Очевидно, это ненормально. Пробуем указать RVA и size, которыми нам предлагала воспользоваться в случае неудачи сама программа, и нажать на кнопку «Get Imports» снова:

    image

    Что-то явно идёт не так, как ожидается. Давайте попробуем найти границы IAT вручную. Ищем в дизассемблированном листинге вызов любой WinAPI-функции. Например, вот этот:

    image

    Прыгаем на него (left click -> Enter) и видим, куда обращаются все JMP'ы:

    image

    Переходим по любому из указанных в данном месте адресов (right-click -> Follow in Dump -> Memory address), выбираем соответствующее представление (right-click по окну Memory Dump'а -> Long -> Address) и видим следующее:

    image

    Пробегаемся глазами до начала списка адресов функций:

    image

    Нажимаем двойным кликом мыши на ntdll.RtlDeleteCriticalSection и ищем конец списка:

    image

    Указываем адрес начала IAT (0005F168) и size (0000066C), снова нажимаем на кнопку «Get Imports» и получаем следующий результат:

    image

    Уже лучше, но всё равно остались невалидные импорты, причём самое странное, что мы вроде бы всё сделали правильно… Давайте воспользуемся другим приложением для восставления IAT — Scylla:

    image

    Да что такое происходит? А давайте попробуем проделать то же самое в другой системе — например, в Windows 7 x32 (до этого эксперименты проводились в Windows 8 x64).

    Чудесным образом мы получили валидные импорты:

    image

    Честно говоря, не уверен, что это — баг ImpRec'а и Scylla, особенность Windows 8 x64 или что-то ещё, но главное, что мы восстановили IAT. Точнее, для этого нам надо нажать на кнопку «Fix Dump», выбрать сделанный ранее дамп и попробовать запустить получившийся исполняемый файл. Да, при запуске говорилки без аргументов она работает, как и положено, а вот в случае передачи каких-либо фраз для озвучивания она падает, как и в запакованном варианте.

    Итак, ASPack снят. Что дальше? А дальше, собственно, нам предстоит разобраться с возникающим на некоторых компьютерах багом.

    Первым делом давайте посмотрим на стандартное окно Windows, сообщающее о краше приложения:

    image

    Вам должно быть заметно, что Exception Offset равен 0x000591D4. Загружаем говорилку в OllyDbg, ставим софтварный бряк на этот адрес (точнее, на image base + 0x000591D4 — в моём случае это 0x004591D4), нажимаем F9 и получаем следующее:

    image

    Как вы видите, значение регистра ESI на момент выполнения операции чтения по адресу ESI+38 равно нулю, что, разумеется, приводит к крашу приложения. Двумя инструкциями выше заметно, что в ESI значение попадает из регистра EAX. Значение регистра EAX до этого в данной процедуре не меняется, так что смотрим по Call Stack'у, откуда нас позвали:

    image

    Прыгаем туда (right click -> Show Call) и видим, что прямо перед вызовом процедуры находится команда MOV EAX,DWORD PTR DS:[45EC5C]:

    image

    Как видно из предыдущего скриншота, по адресу 0x45EC5C также находится ноль. Ставим хардварный бряк на запись по этому адресу (right click -> Breakpoint -> Hardware, on write -> Dword), запускаем приложение ещё раз и обнаруживаем, что никакой записи по данномуадресу не происходит. Давайте проанализируем, как себя ведёт приложение в случае успешной работы в другой системе. Запись по адресу 0x45EC5C в этом случае действительно происходит:

    image

    , в результате чего ESI+38 даёт какое-то осмысленное значение. Давайте узнаем, как мы попали в это место. Call Stack на момент выполнения данной инструкции пустой, так что у меня возникло впечатление, что это основная процедура программы. Чтобы понять, в каком именно месте приложение стало вести себя по-другому, давайте запустим Trace over (Ctrl-F12) на обеих системах (там, где приложение падает, и там, где оно работает).

    В случае системы, где приложение постоянно падает, оно достигло бряка на доступе к ESI+38, где и упало. Последняя строчка кода в trace log'е была CALL'ом по адресу 0x004597C8:

    image

    , в то время как в системе, где приложение работает нормально, после вызова данной процедуры (0x004597C8) происходит выполнение других инструкций, на одной из которых и срабатывает хардварный бряк на запись по адресу 0x45EC5C:

    image

    Возможно, что-то идёт не так в процедуре 0x004597C8. Ставим софтварный бряк на её вызов (0x0045AD5B) и обнаруживаем, что его установка ведёт к тому, что на той системе, где всё работало нормально, теперь не срабатывает хардварный бряк на запись по адресу 0x45EC5C, в результате чего приложение падает, как и в случае другой системы. С хардварными бряками на выполнение всё обстоит так же.

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

    image

    Возможно, установкой бряков в данных местах мы мешали замерам времени, которые как раз и осуществляются при помощи вызовов функции GetTickCount.

    Запускаем Trace into (Ctrl-F11), начиная с данной инструкции, и обнаруживаем, что различия в выполнении начинаются вот с этого места:

    ; если приложение выполняется в системе, где оно падает
    004597EC Main     MOV EAX,DWORD PTR DS:[45EC98]             ; EAX=F2B47801
    004597F1 Main     ADD EAX,64                                ; EAX=F2B47865
    004597F4 Main     CDQ                                       ; EDX=FFFFFFFF
    004597F5 Main     CMP EDX,DWORD PTR SS:[ESP+4]
    004597F9 Main     JNZ SHORT Govorilk.00459807
    00459807 Main     POP EDX                                   ; EDX=F2B47802
    

    ; если приложение выполняется в системе, где оно нормально работает
    004597EC Main     MOV EAX,DWORD PTR DS:[45EC98]
    004597F1 Main     ADD EAX,64                                ; EAX=064A1501
    004597F4 Main     CDQ
    004597F5 Main     CMP EDX,DWORD PTR SS:[ESP+4]
    004597F9 Main     JNZ SHORT Govorilk.00459807
    004597FB Main     CMP EAX,DWORD PTR SS:[ESP]
    

    Отсюда заметно, что результат выполнения инструкции CMP EDX,DWORD PTR SS:[ESP+4] влияет на дальнейшую работу процедуры. В случае системы, где оно работает, флаг Z регистра флагов был выставлен на момент выполнения данной инструкции, в то время как в случае той системы, где приложение падает, данный флаг не был выставлен:

    image

    Давайте посмотрим, что же на это влияет. Итак, что у нас здесь происходит?

    • Вызывается функция GetTickCount, результат её вызова записывается в регистр EAX
    • Обнуляется содержимое регистра EDX
    • Содержимое регистров EDX (0x0) и EAX (результат выполнения функции GetTickCount) заносятся на стек
    • В регистр EAX помещается значение из адреса 0x45EC98
    • К нему прибавляется 0x64
    • Выполняется инструкция CDQ
    • И, наконец, сравнивается содержимое регистра EDX и значение по адресу ESP-4, где хранится предыдущее значение данного регистра, т.е. 0x0

    Поставив хардварный бряк на запись по адресу 0x45EC98, можно увидеть, что туда также помещается результат выполнения функции GetTickCount:

    image

    Кстати, а почему GetTickCount возвращает такое большое значение? Ведь система выключалась мной ещё сегодня утром. На самом деле, тут играет свою роль hybrid boot и shutdown в Windows 8. Почитать об этом больше можно, например, тут.

    Но вернёмся к основной теме нашего обсуждения. В чём же тут тогда проблема? Давайте внимательно посмотрим на содержимое регистров общего назначения в этом куске кода на обеих системах. Вам должно броситься в глаза, что после выполнения инструкции CDQ на системе, где приложение работает корректно, регистр EDX принимает значение 0x0, а в системе, где программа падает, 0xFFFFFFFF, что видно, например, на предыдущем скриншоте. Внимательно читаем описание инструкции CDQ:

    Converts signed DWORD in EAX to a signed quad word in EDX:EAX by extending the high order bit of EAX throughout EDX

    А теперь взглянем на сигнатуру функции GetTickCount:

    DWORD WINAPI GetTickCount(void);
    

    Как видите, она возвращает DWORD, который «раскрывается» в unsigned long, т.е. беззнаковый тип.

    Уж не знаю, баг это разработчика или компилятора, но на такое поведение автор явно не рассчитывал, ведь в текущей ситуации говорилка будет падать на системах, которые работают дольше 24.8 и меньше 49.7 дней. Почему именно так? Нижняя граница определяется максимальным значением, которое может поместиться в знаковую 4-байтовую переменную — 2147483647:

    2147483647 / 1000 / 60 / 60 / 24 = 24.8551348032

    Верхняя же граница определяется самой функцией GetTickCount, о чём сказано в документации (собственно, она определяется максимальным значением, которое может поместиться в беззнаковую 4-байтовую переменную):

    The elapsed time is stored as a DWORD value. Therefore, the time will wrap around to zero if the system is run continuously for 49.7 days

    Ну что, пора исправить это допущение. Один из вариантов исправления проблемы — каким-то образом «подменить» вызов функции GetTickCount своим кодом, в котором мы будем «уменьшать» возвращаемое из неё значение настолько, чтобы оно поместилось в знаковую 4-байтовую переменную.

    Вариантов решения данной задачи несколько, но давайте напишем code cave. Ищем свободное место (проще всего найти его в «конце» окна CPU) и помещаем туда следующий код:

    ; Сохраняем состояние регистров общего назначения на стеке
    0045BAAA      60               PUSHAD
    ; Сохраняем состояние регистра флагов на стеке
    0045BAAB      9C               PUSHFD
    ; Получаем значение EIP
    0045BAAC      E8 00000000      CALL Govorilk.0045BAB1
    0045BAB1      5E               POP ESI
    ; Вызываем функцию GetTickCount
    0045BAB2      FF96 2F380000    CALL DWORD PTR DS:[ESI+382F]
    ; Сохраняем результат её выполнения на по адресу ESP-4
    0045BAB8      894424 FC        MOV DWORD PTR SS:[ESP-4],EAX
    ; Восстанавливаем состояние регистра флагов
    0045BABC      9D               POPFD
    ; Восстанавливаем состояние регистров общего назначения
    0045BABD      61               POPAD
    ; Помещаем в регистр EAX результат выполнения функции GetTickCount
    0045BABE      8B4424 D8        MOV EAX,DWORD PTR SS:[ESP-28]
    ; Выполняем операцию битового "AND" над значением, хранящемся в регистре EAX
    0045BAC2      25 FFFFFF0F      AND EAX,0FFFFFFF
    0045BAC7      C3               RETN
    

    Значение 382F получилось в результате вычисления разницы между адресом в IAT, по которому хранится адрес функции GetTickCount (0x0045F2E0), и текущим адресом (0x0045BAB1).

    Получить значение регистра EIP напрямую не получится — к нему нельзя обращаться ни на чтение, ни на запись.

    Теперь переходим на место, где осуществляется JMP на функцию GetTickCount

    image

    и заменяем адрес, на который прыгает JMP, на адрес начала нашего code cave'а (в моём случае это 0x0045BAAA):

    image

    Сохраняем изменённый исполняемый файл (right-click по окну CPU -> Copy to executable -> All modifications -> Copy all -> right-click на появившемся окне -> Save file) и проверяем его работоспособность. Да, он действительно работает, однако при запуске говорилки теперь выдаётся знакомое практически всем пользователям Windows окно UAC. Почему именно это происходит можно почитать, например, тут, а для решения проблемы в нашем случае достаточно всего лишь дать нашему исполняемому файлу оригинальное название — «Govorilka_cp.exe».

    Послесловие


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

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

    Похожие публикации

    Комментарии 53

      +26
      Хех. Припоминаю ситуацию, когда работал реверс-инженером в фирме по локализации. Исходников проекта не было.
      И вот, уже пора сдавать проект. И менеджер заказчика решил выпендриться и в списке багов написал баг, который изначально в игре присутствует и к локализации никакого отношения не имеет.
      Ну чтож. Вызов приняли. Выковыряли скрипты, аккуратно разобрались в логике работы и исправили баг. Было весело. :)
      P.S. Не знаю зачем я это всё написал. :(
        –1
        Я понимаю, в таких темах к терминологии придираться не comme il faut, но как обьяснить происхождение термина «бряк», от оригинала как по произношению так и по написанию он довольно далек. Тем паче, что вполне можно найти аналог и из незаимствованных слов
          +12
          Наверное, происхождение объяснить можно так:
          — брейк произносить не так удобно
          — в русском языке есть слово «бряк», похожее по произношению и по смыслу
          Найти можно, но принято так — или, как вы говорите, в этом контексте слово «бряк» (а кто-то и «бряка» говорит) — как раз-таки комильфо, причём уже очень давно.
            +1
            бряк — наше все, сколько себя помню бряк именовали именно бряком и никак иначе
              0
              Брык.
                +1
                Ни разу не встречал такого написания или не помню просто. Даже гугл плохо помнит: по «ставим брык» нашел 1 (один) результат, а по «ставим бряк» — 4 570
                  +2
                  Останов.
                  Устаревшее, но используется до сих пор. «точка останова» (breakpoint).
                  Ха, даже в вики есть: ru.wikipedia.org/wiki/%D0%A2%D0%BE%D1%87%D0%BA%D0%B0_%D0%BE%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%B0
                    0
                    Ну это понятно, что есть «нотариально заверенный перевод», но изначально здесь речь зашла о том «откуда есть пошел бряк» и вообще вокруг него. Бряк устоявшееся сленговое понятие, значение которого понятно всем, кто в теме. Точки останова — уровень книг и MSDN. Бряк — разговорный стиль скорее.

                    Ну с бряком-то вроде разобрались, а вот «булевая» vs «булевская» вопрос не раскрыт ;)
                      +1
                      А может быть, «булева»? По аналогии с алгеброй, функцией и т.п.
                        0
                        Да может и так, я исследования не проводил, а субъективно эти три написания встречаются примерно одинаково.
                          –1
                          Булева — это типичное сокращенное прилагательное от «булевая», так же, как «красна» от «красная», «симпатична» от «симпатичная», и т.п.
                            +1
                            Тем не менее, математики говорят именно «булева алгебра», а не «булевая алгебра». И это вполне естественно: поскольку Джордж Буль, в честь которого она названа — имя собственное, то «булева» — никакое не прилагательное, а притяжательное местоимение: «принадлежащая Булю». И, соответственно", никакого слова «булевая» в русском языке нет.
                              0
                              А вот слово «булевская» вполне возможно — есть же слова «петровский», «екатерининский», «андреевский»…
                          +1
                          Логическая :)
              –18
              Вот чем мне нравится язык SmallTalk что никакой реверс — инженеринг в его случае не поможет. Соответственно невозможно создать ни кряк, ни взломщик. Правда багу при отсутствии исходников то же невозможно исправить. А недавно так нужно было…
                –3
                Ну естественно «настоящие хакеры» испугались упоминания языка программу на котором нельзя сломать, и быстренько заминусовали. Теперь уже дело чести написать пост об этом незаслуженно неизвестном в России языке программировании, который с моей точки зрения является наиболее удобным и гибким воплощением ООП.
                  +5
                  А вот напишите. Мне интересно. У компилятора что, какие-то особенные волшебные опкоды получаются, которые ни один дебаггер не знает?

                  P.S.: не голосовал.
                    –5
                    Как раз сейчас этим и занимаюсь. В основном проекте нарисовался небольшой перерыв — появилась возможность. К понедельнику думаю сделаю.
                    По Вашему вопросу. Декомпилировать получится только виртуальную машину, соответственно изменить исходный код получится только её. Исходный код программы находится в отдельном образе — хранилище классов, и он представляет собой бинарник, структуру которого я думаю знают только в Sincome. И вот его уже никакой декомпиляцией не измениш.
                      +6
                      По вашей логике, любой язык который использует ВМ невозможно исследовать. А при этом, люди Another World разобрали, а там как раз была ВМ и так же не было исходных кодов.
                        –4
                        Возможно, но честно говоря я не слышал о такой возможности. Я работаю со Smalltalk — ом уже почти 10 лет, но ни разу не слышал о возможности дебажить упакованный образ. Там в принципе даже не команды ассемблера, а байт- код, инструментов для просмотра которого насколько я знаю не существует.
                          +18
                          Ну вот мы и пришли к правильной формулировке вашего комментария — «Я не слышал о такой возможности»
                            +2
                            Вы несколько наивно пишете о том, что как будто нельзя разобрать формат бинарного файла.

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

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

                            P. S. я абсолютно не знаком с данной средой.
                              +1
                              Замечу, что в комплекте этой среды дополнительно идет отладочная VM, в которой полно ассертов, логов и т.д., но главное, что она еще собрана с полной отладочной информацией (специально, чтобы разработчик мог нормальный стектрейс представить авторам).

                              Понятно, что это означает для исследователя.
                          +5
                          Я вам даже подскажу кто знает структуру того тайного бинарника с исходным кодом. Это сама ВМ.
                            –7
                            Не думаю что в Sincome не озаботились возможностью разборки своей BM. Ребята работают в основном с банковской сферой, так что там думаю все серьёзно. Но хотя можете при желании попробовать.
                              0
                              Ну это вполне объясняет отсутвие широкой информации о данной ВМ. Такие вещи обычно в общий доступ не идут.
                                0
                                Я как раз сейчас пишу пост об этом языке, и там расскажу его историю. Вообще то этот язык как бы не старше большинства из существующих. Первая реализация — 71 год.
                                  +1
                                  Пишите конечно. Но наличие GNU Smalltalk подразумеват наличие исходных кодов. Так что мы приходим к тому, что скорее всего отсутствует спецификация конкретно ВМ Sincome.
                                +5
                                По-моему, вы демонизируете Смоллток :-)

                                Посмотрите класс CompiledCode, Compiler, Decompiler, InstructionStream и прочие. Учитывая рефлексию как неотъемлемое свойство смоллтока, можно получить просто огромное количество информации на достаточно низком уровне. В отданном экзешнике многих этих классов, вероятно, не будет (если он умен, чтобы их стрипнуть), но их достаточно, чтобы изучить байткод и вообще организацию компиляции и рантайма. Да, VM там JIT, но делает она native code все-таки из байткода…

                                Вот вам и байткод, и декомпиляция байткода средствами самого же Cincom VisualWorks (извините за стиль, слишком давно на нем не писал):

                                t := [ :arg | arg*2 ] // в момент присвоения в переменную t, этот блок скомпилировался
                                
                                t value: 100 // проверяем, что работает
                                >> 200
                                
                                t method bytes // получаем байт-код
                                >> #[16 75 168 101]
                                
                                result := String new writeStream.
                                t method printSymbolicOn: result.  // печатаем "дизассемблированный" байткод
                                result contents
                                >> 'normal CompiledBlock numArgs=1 numTemps=0 frameSize=12
                                >>
                                >>literals: ()
                                >>
                                >>1 <10> push local 0
                                >>2 <4B> push 2
                                >>3 <A8> send *
                                >>4 <65> return
                                >>'
                                
                                t method decompiledSource // просим декомпилировать байткод
                                >> '[:t1 | t1 * 2]'
                                
                                // печатаем декомпилированный метод asOrderedCollection класса Collection
                                Collection enumerateMethods: [ :class :selector :method | selector asString = 'asOrderedCollection' ifTrue: [ Transcript nextPutAll: method decompiledSource ]]
                                ....и т.д.
                                
                              +9
                              «security through obscurity» что-ли?
                                +3
                                Да нет, просто никому не нужно (очень хороший язык, но вытеснен в совсем уж узкую нишу)
                            0
                            >Ну естественно «настоящие хакеры» испугались упоминания языка программу на котором нельзя сломать…

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

                            PS Интересно, насколько сложно ломать LISP и прочую функциональщину, человеку с функциональными языками незнакомому.
                              +1
                              Да элементарно. LISP, в общем-то, штука очевидная. Алсо, товарищ totuin, взломать можно все, кроме шифра Вернама, было бы желание. Единственная сложность с VM — под них приходится писать все инструменты самому.
                              0
                              А что, для JAVA машин нет декомпилятора?
                              +1
                              Не могли бы вы объяснить, почему реверс-инженеринг не поможет?

                              (не обновил комментарии, когда писал свой; с удовольствием почитаю пост на эту тему)
                                0
                                Чуть выше написал. Пока писал — появился Ваш коментарий
                                +3
                                Боюсь, Вы просто не знакомы с темой защиты кода от реверсинга.
                                Язык высокого уровня тут вообще ни при чем, «вообще» — от слова «совсем».
                                Хакеры работают с машинным кодом.
                                Хоть Вы там вложенный интерпретатор Smalltalk'а на Smalltalk'е сто раз подряд реализуйте — это только несколько затруднит процесс разбора (придется написать кучу скриптов и их отладить на кошках).

                                1) Не первый десяток лет для защиты бинарников их код транслируют из машинного кода целевой архитекутры в машинные коды специальных обфусцированных VM. Самый известный пример: VMProtect (с 2000 года по наши дни).

                                2) Референсная реализация VM любого интерпретируемого языка заведомо проще, чем такие специализированные VM более-менее современных «пакеров»/«протекторов». Машина SmallTalk — детский лепет по сравнению с тем же VMProtect (у которого в последних версиях набор инструкций уникален для каждого бинарника, например).

                                3) Но и их, при всей их сложности, успешно исследуют и далее ломают с самого начала; ломали, ломают и ломать будут. Некоторый код в любом случае выполняется целевым железом, значит, его можно на уровне железа сдампить и вручную выполнить, а значит, и понять. Увидели там виртуальную машину? ОК, пошагово разобрали ее, ее архитектуру, набор инструкций и т.д. То, что долго делать руками, давно и успешно автоматизируется.

                                4) Единственный относительно надежный способ защиты кода от исследования — особая целевая архитектура с шифрованием «на кристалле».
                                Это реальная железная машина, напрямую выполняющая зашифрованный машинный код, ключи расшифрования для которого не покидают ее защищенную память. В идеале, ключи уникальны для каждого процессора и для каждого бинарника.
                                О защите кода начали задумываться еще в семидесятые-восьмидесятые, основные идеи можно найти в статьях того времени, да и этот вывод там тоже есть:
                                Best R. M. Preventing software piracy with crypto-microprocessors //Proceedings of IEEE Spring COMPCON. – 1980. – Т. 80. – С. 466-469.

                                На практике нечто отдаленно похожее сейчас массово (~миллиард штук) реализовано в банковских чиповых картах стандарта EMV. На карту от банка-эмитента может прийти зашифрованный issuer script, который содержит, на самом деле, не машинный код, а простенькие аппликационные команды вида «разблокировать оффлайн PIN» или «установить тег X в значение Y», но ключ шифрования, которым закрыты эти команды, знает только карта и банк.
                                +7
                                Пост автора вызывает уважение. А вы автора Говорилки вы просили открыть исходный код программы?
                                На странице программы даже указаны его фамилия, имя и почтовый адрес (Anton Ryazanov, vecs@mail.ru). Ну то есть если автор поддерживает программу, то он должен был сам исправить баг, а если не поддерживает, то почему не открыть исходный код под одной из свободных лицензий?
                                  +1
                                  Всё жду когда кто-нибудь исправит баг проводника, когда при копировании и вставке файлов диалог замены появляется не поверх всех окон, а где-то там позади. Даже в последнем на данный момент билде windows 10 он есть. Ну а что, звукозапись же фиксили.
                                    +5
                                    Опять же повторюсь — удивительный мир хакеров. Заставить комп под виндой говорить человечьим голосом с помощью спич апи — задача на десяток строк кода на любом языке, какой есть. Во всяких скриптовых языках — на две строки. Что может сподвигнуть вместо этого пользоваться чужой неподдерживаемой прогой с багами? Загадка.
                                      +10
                                      Без этой программы невозможно представить некоторые видео :)
                                      0
                                      Интересно, познавательно и легко написано.
                                      Всегда хотел также легко писать ASM, и знать адреса, смещения, IAT и фокусы отладки (хотя прошел Рикардо до 40 главы, забывается когда не используешь повседневно). Но выбрал C#, а там свои чудеса.

                                      Жду пост о «SmallTalk», интересно.
                                      +3
                                      Не вижу причин делать бесплатную программу closed-source, кроме как из-за стыда показать свой код.
                                        0
                                        Судя по сайту, эту программу написали лет семь-десять назад, когда о Open Source еще не все знали.
                                          0
                                          Основная причина — что для этого надо как-то изучить экосистему Open Source — что, куда выкладывать, как, в каком формате, какие там обычаи и т.п. Изучить это один раз, чтобы выложить одну программу, а через 10 лет изучать снова — смысла не видно. А чтобы жить в этой среде, нужны дополнительные ресурсы. Которые можно было бы пустить на что-нибудь другое.
                                          Порог вхождения не может быть нулевым.
                                          0
                                          Хотелось бы статью о том как расшифровывают форматы файлов без исходных кодов.
                                          +1
                                          После этой статьи я ещё больше полюбил OpenSource и Free Software…
                                            0
                                            image
                                            Как-то маловато «всей правды о багах».

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

                                          Самое читаемое