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

может что то и получиться а если нет то все равно будет опыт
Декодер ionCube — как работает сейчас и что осталось
Задача
Есть зашифрованные ionCube-файлы PHP (модули CMS, аддоны). Нужен читаемый исходник, чтобы разбираться с логикой и править её. Локально доступен только лоадер ionCube — «выпотрошить» файл без запуска не удалось: формат не документирован.
Цель — не привязка к одной версии PHP, а универсальный разбор: чтобы одна и та же логика работала на любой установленной версии с соответствующим лоадером. Поэтому всё, что зависит от версии, вынесено в отдельные профили, а ядро остаётся общим.
Главная идея: не ломать шифр, а подсмотреть
Первая мысль — расшифровать контейнер офлайн, разобрать файл как структуру данных. Она упирается в принципиальный момент: внутри не лежит исходник и даже не нормальный байткод Zend. Файл скомпилирован в байткод и зашифрован, и при запуске лоадер расшифровывает его на лету, исполняя через собственную виртуальную машину, а не через штатный механизм PHP. Исходника в памяти нет ни в каком виде — ни обфусцированного, ни частичного. Искать его бессмысленно, ловить в куче или в opcache тоже: компиляция идёт мимо Zend, кэш её не видит.
Поэтому стратегия другая: запустить файл в обычном PHP с обычным лоадером и снимать данные в момент, когда лоадер их уже расшифровал. Файл при этом исполняется штатно, а мы ставим наблюдение в самый горячий участок — в тот момент, когда виртуальная машина лоадера выбирает обработчик очередной инструкции. Это единственная точка, где одновременно доступны номер инструкции, её адрес, сырые байты записи и выбранный обработчик. Отсюда снимается всё остальное.
Конвейер концептуально состоит из четырёх слоёв:
Подсмотр. Перехват в цикле виртуальной машины лоадера: снимаем содержимое всех служебных структур — массивов инструкций, пулов литералов, ключевых потоков.
Расшифровка. Восстановление номеров операций: сырой байтовый поток превращается в таблицу «обработчик → номер операции».
Реконструкция. Сборка структуры: имена методов, строки, аргументы, ветвления — обратно в PHP-код.
Проверка. Синтаксическая проверка результата и сравнение с эталоном, если он есть.
Ядро (слои 1, 3, 4) от версии PHP не зависит. Версионное — только слой 2: набор номеров операций у каждой версии свой, поэтому он хранится как сменяемый профиль.
Ключевые находки
1. Ключевой поток. Номера операций в файле не хранятся открытыми. Вместо них лежит «обработчик» — адрес функции виртуальной машины, зашифрованный потоком байтов, уникальным для каждой функции. Формула восстановления найдена и подтверждена на всех проверенных сэмплах: ключ двойного слова собирается из четырёх байт потока по простой формуле, а сама операция получается обратным XOR. Один этот шаг дал полную статическую расшифровку всех операций без всякого исполнения файла — то есть по сути найден способ читать содержимое файла, даже не запуская его.
2. Карта «обработчик → операция». У Zend для каждой операции существует своя функция-обработчик, значит, их адреса можно сопоставить с распознанными обработчиками. Так составлена карта соответствий; редкие операции ветвлений размечены по контексту, потому что напрямую они не идентифицируются. Дополнительно используется автоякорь: первая операция любой функции всегда одна и та же (приём аргументов), и она служит проверкой «плавающей базы» между прогонами — если якорь съехал, весь последующий разбор сдвинут. Эта же схема — то есть сам способ построения карты, а не сама карта — переносится на любую версию: для нового профиля нужно только пересобрать соответствия.
3. Пул литералов — это поток исполнения. Главный сюрприз и главная потеря времени. Литералы (строки, имена) материализуются лениво: в памяти они появляются только тогда, когда до них дошла исполняющаяся функция. Ранний дамп снимал пулы до исполнения, и в 97 из 103 функций они были пустыми. После переноса снятия дампа после исполнения с принудительным вызовом всех методов пулы заполнились полностью.
Дальше выяснилось, что порядок литералов в пуле совпадает с порядком их первого использования в коде. Отсюда появился принцип «курсора»: разбор идёт по инструкциям, и при каждом обращении к литералу берётся следующий по порядку. Простые правила: строка и её вариант в нижнем регистре — один литерал; одиночная нижняя копия — мусорный дубль прошлой строки; скаляр занимает один слот; дальше граница, за которой начинается чужая память, — стоп.
4. Ветвления. Операнды переходов зашифрованы в статическом дампе, но истинные значения видны только в момент исполнения. Поэтому значения снимаются отдельным наблюдением и складываются в отдельный слой данных. Цель перехода при этом считается по простой формуле от индекса инструкции и второго операнда — подтверждено 28 парами трассировки. Сами обработчики ветвлений опознаются по трём семействам значений.
5. Имена вызовов. В самих операциях имён методов нет вообще. Они берутся из литералов по эвристике «ближняя пара имя/имя-метода» — для чистых методов она попадает точно. Отдельно вскрыт паттерн одного из форматов: последний вызов аргумента в нём физически стоит после вызова функции, то есть при разборе его нужно забирать «взглядом вперёд».
6. Воспроизводимость. Снятые данные сохраняются полностью, и весь разбор потом идёт по ним, без повторного запуска зашифрованного кода и без инструментов в реальном времени. Это оказалось важнее, чем кажется на первый взгляд: иначе каждый виток исследования зависел бы от запуска на живой машине с живой лицензией, и результаты были бы невоспроизводимы.
Что реально работает сейчас
Этап Статус
Формат контейнера, дескриптор, заголовок разобран полностью
Расшифровка операций (ключевой поток) 100%
Карта «обработчик → операция» ~94% операций именовано
Пулы литералов 52/52 и 103/103 функций полные
Ветвления (if / else) восстанавливаются, но только по исполненным веткам
Генерация PHP-кода оба файла проходят синтаксическую проверку
Совместимость со старым форматом ionCube 10.2 работает без изменений конвейера
Ядро конвейера версионно-независимо; проверено на нескольких установленных версиях PHP с соответствующими лоадерами, переключение сводится к смене профиля операций.
Качество на реальных файлах
Чтобы мерить точность, взяты референсные декоды тех же самых файлов сторонним платным декодером. Сравнение смысловой близости — по вызовам, строкам, числам и ветвлениям:
Образец Формат Методов Средняя точность
A — большой CRUD-модуль 45bfa667 101/101 84% (57 методов ≥95%)
B — модуль авторизации 45bfa667 51 / 41 в референсе 34%
C — маленький служебный класс ionCube 10.2 6 / 11 36%
Отдельные методы восстанавливаются почти идеально. Например, HTTP-хелпер из образца B повторяет референс один в один по структуре (инициализация сессии, семь вызовов настройки, два условия) — расходятся только имена переменных. Простой метод-вставка из образца A — 100%.
По форматам выяснилось важное: в старом ionCube 10.2 имена переменных лежат открытыми и извлекаются тривиально, в новом 45bfa667 они зашифрованы отдельным слоем — при одинаковом коде конвейера это разные объёмы работы.
Что не решено
1. Имена переменных в формате 45bfa667 — главный открытый вопрос. Записи имён зашифрованы даже в рантайме: длина фиксированная, содержимое — мусор. Два пути: либо реверс формулы расшифровки (нужны известные plaintext-примеры и ключевые потоки для подбора), либо перехват в момент, когда виртуальная машина сама использует имя — например, при выводе ошибки или фатального сообщения, где имя попадает в текст. Второй путь выглядит реалистичнее, но это отдельный проект по наблюдению.
2. Аргументы вызовов в формате v11. В образце B константы и имена идут не напрямую из пула, а через промежуточные кэш-слоты; слоты монотонно убывают, а группы литералов идут в порядке потока. Правило «k-й слот соответствует k-й группе» пока не подтверждено на всём файле — отсюда 34% по образцу B при 84% по образцу A. Это не дефект генератора, а нерешённая задача разбора.
3. Аргументы строго по номеру позиции. Сейчас раскладка идёт по порядку потока исполнения, из-за чего аргументы местами съезжают — заметнее всего на длинных методах вроде отмены подписки и приёма платежа. Нужна строгая раскладка по номеру аргумента, который операция передачи знает точно.
4. Неисполненные ветки. Значения операндов берутся из пробы, а проба не может покрыть всё: нужна реальная сессия, пользователь, база. Ветка, которая не исполнилась, остаётся закомментированной. Сейчас сделано 5 проходов аргументов (пусто / единица / строка / массив / истина); дальше нужны пробы с состоянием CMS.
5. Константы вида C0x и пустые значения. Индексы из общей таблицы констант виртуальной машины, которая лежит вне литерального пула. Часть значений вытаскивается из трассы исполнения — так вскрылись языковые константы и набор эндпоинтов внешнего OAuth-провайдера, — но остальное осталось нетронутым.
6. Циклы foreach/fetch. Операторы цикла в декодированном наборе отсутствуют: соответствующие опкоды не восстановлены, поэтому циклы в выводе не появляются.
7. Неполнота карты операций. Часть редких обработчиков размечена по контексту с риском ошибки — например, метки одной операции внутри HTTP-вызова стоят неверно, а роль части обработчиков подтверждена только трассировкой, а не формулой.
8. Профили под остальные версии PHP. Ядро и способ построения карты уже версионно-независимы, но набор профилей покрывает не все установленные версии. Чтобы расширить покрытие, для каждой новой версии нужно пересобрать карту соответствий и повторить контрольный декод одного файла — процедура отработанная и не требует изменений в логике разбора.
9. Альтернативный формат обмена с готовым лифтером — не начат. Собственный генератор покрывает текущие задачи, поэтому отдельный слой сериализации пока не нужен.
Практический итог
Декод пригоден для анализа логики, но пока не для продакшена. Типичный результат: структура класса, сигнатуры, имена методов, строки, SQL-подобные вызовы и логика ветвлений читаются; для точного воспроизведения поведения нужна ручная доводка конкретного файла. По референсу: образец A — 84% при 100% покрытии методов, образец B — 34%. Это честная картина.
Два практических совета:
декодировать имеет смысл файлы старого формата (ionCube 10.2 и раньше) — там имена переменных сразу открыты и результат заметно чище;
архитектура конвейера не привязана к версии PHP: переход на другую версию — это смена профиля операций, а не переписывание разбора.
Можно закидать тапками сказать что я тупой нуб и лучше заняться чем то еще
