stihl не предоставил(а) никакой дополнительной информации.
Сегодня мы разберемся с тем, как устроена «непробиваемая» защита VBA-скриптов в Excel, предлагаемая компилятором DoneEx VBA Compiler. Мы посмотрим, как этот инструмент компилирует макросы в нативные DLL, какие трюки использует для контроля целостности, и шаг за шагом покажем, как обходить проверки и восстанавливать исходный код из скомпилированных модулей.
Ты, вероятно, обращал внимание на забавный эффект: как только нуб выучит какой‑нибудь простецкий язык в достаточной степени, чтобы самому писать скрипты, его охватывает всепоглощающее чувство собственного величия, переходящее в желание продавать свои поделки окружающим. Этому обычно сопутствует опасение, как бы эту бесценную интеллектуальную собственность не украли. Ничего плохого в этом нет, поскольку такой страх дает работу целому сегменту программистов, пишущих защиты для скриптов разной степени упоротости.
Я уже писал статьи про подобные защиты, разработанные на JavaScript, Python, PHP и даже AutoIT. Сегодня у меня наконец‑то дошли руки до темы защиты экселевских VBA-скриптов.
Недавно я наткнулся на известный в очень узких кругах компилятор Для просмотра ссылки Войдиили Зарегистрируйся. Он позиционируется ни много ни мало, как полноценный компилятор Excel VBA в нативный код со встроенной защитой. Сразу предупреждаю: это поделие достаточно кривое — лично мне, чтобы скомпилировать на нем хоть какой‑то более‑менее работоспособный пример, понадобилось гораздо больше времени, чем разобраться, как снимать саму защиту. Однако ее описание изобилует громкими претенциозными заявлениями вроде «код VBA не может быть скопирован или восстановлен и имеет самый высокий уровень защиты от пиратства». Давай попробуем оспорить это утверждение.
К слову сказать, эти ребята еще и Для просмотра ссылки Войдиили Зарегистрируйся, который, правда, без установленного Excel не запускается, но это совсем другая история, которой мы сегодня касаться не будем.
Итак, предположим, что нам в руки попал необычный экселевский файл. К нему прилагается одна (или две) DLL-библиотека с тем же именем, но возможны суффиксы *_64 или *_32 в зависимости от разрядности. Если натравить на них Detect It Easy, программа показывает компилятор MinGW(GCC: (GNU)), однако ординалы из них экспортируются довольно подозрительные.
Для просмотра ссылки Войдиили Зарегистрируйся
Попробуем открыть защищенный документ. На экране появляется весьма раздражающее окно с бегущим ползунком, а затем выскакивает предложение о регистрации.
Для просмотра ссылки Войдиили Зарегистрируйся
Пока оно висит, откроем наш любимый отладчик x64dbg и приаттачимся к процессу Excel. Принудительно прервав его выполнение, мы видим занятный стек вызовов.
Для просмотра ссылки Войдиили Зарегистрируйся
Из него как на ладони видна последовательность вызовов, порождающих это окно. Вначале непосредственно из VBA-кода вызывается функция SetThisWorkbook, экспортируемая из нашей скомпилированной библиотеки testvba1_xlsm_64.dll. Эта функция вызывает функцию DummyFunc05 некоей загадочной библиотеки cbinrtl.dll, отсутствующей в каталоге макроса и вообще непонятно откуда взявшейся. А уже она выкидывает окно предупреждения, реализованное через функцию DialogBoxIndirectParamW.
При детальном рассмотрении мы обнаруживаем эту библиотеку в подкаталоге v6n3vk66ej (название при каждом вызове случайное) временной папки Windows. Скомпилированный модуль testvba1_xlsm_64.dll сохраняет ее туда из соответствующего собственного ресурса при вызове SetThisWorkbook, а затем подчищает за собой при отработке. Собственно, скомпилированный модуль практически целиком и состоит из библиотеки cbinrtl.dll, хранящейся в нем в явном незашифрованном виде, так же как и в самом модуле компилятора vbaclr4e.exe, от которого ее и получает. Это дает надежду на убиение двух зайцев патчем файла testvba1_xlsm_64.dll — можно патчить код и библиотеки testvba1_xlsm_64.dll, и порождаемой ею библиотеки cbinrtl.dll.
Попробуем это реализовать. Сразу напрашивается гипотеза, что защита сосредоточена в функции DummyFunc05, — ведь именно ее вызов и выводит сообщение о незарегистрированной версии. Однако ее нельзя просто так взять и закоротить — Excel при этом падает с ошибкой. При ближайшем рассмотрении мы видим ее вызовы в IDA. Почему так происходит? Эта функция заполняет некую жизненно важную для программы структуру qword_62FC9180.
Для просмотра ссылки Войдиили Зарегистрируйся
Значит, придется копать вглубь кода DummyFunc05, благо основные вызовы у нас уже размечены на стеке. Довольно быстро мы натыкаемся на развилку в коде cbinrtl.dll, которая ведет к заполнению структуры qword_62FC9180 (на следующем скриншоте она обозначена как a2, поскольку передается вторым параметром в процедуру).
Для просмотра ссылки Войдиили Зарегистрируйся
Функция sub_180047A20 в незарегистрированной версии выкидывает окна c предупреждениями, однако, если скомпилировать модуль без защиты, эта функция просто возвращает 1 без лишних слов. Если в отладчике закоротить ее на return 1, все тоже работает безо всяких предупреждений, но при попытке патча модуля testvba1_xlsm_64.dll нас ждет сюрприз. Находим во вложенном в файл testvba1_xlsm_64.dll модуле cbinrtl.dll место, соответствующее sub_180047A20, и патчим его нужным образом.
Для просмотра ссылки Войдиили Зарегистрируйся
Однако патченный файл упорно отказывается загружаться, хотя, как я уже говорил, при патче непосредственно в отладчике все работает корректно. Налицо встроенный контроль целостности. Попробуем его открутить.
Первое, что бросается в глаза, — проверка 128-битного хеша SHA-1 непосредственно перед загрузкой cbinrtl.dll. Обрати внимание на верхнюю часть скриншота — загрузка модуля происходит только тогда, когда функция sub_62FC28D4 дает добро.
Для просмотра ссылки Войдиили Зарегистрируйся
Внутри она реализована так.
Для просмотра ссылки Войдиили Зарегистрируйся
Функция sub_62FC27F7 читает весь модуль, sub_62FC2095 считает его хеш, который затем сравнивается с тестовым значением при помощи memcmp (его возвращает sub_62FC1720). Эту проверку легко можно закоротить, однако ей дело не ограничивается. Если мы это сделаем, то модуль cbinrtl.dll загружается, патченная функция DummyFunc05 корректно отрабатывает, однако в самом конце SetThisWorkbook вылетает по эксепшену при вызове некоего безымянного callback из того же cbinrtl.dll.
Для просмотра ссылки Войдиили Зарегистрируйся
В непатченном варианте указатель на функцию qword_62FC90B0 содержит совершенно другой адрес, который отрабатывает нормально. Помнишь, в начале нашего повествования я упоминал некую жизненно важную структуру qword_62FC9180, заполняемую в DummyFunc05? Она как раз и содержит этот адрес и, если модуль пропатчен, заполняется некорректно. Разработчики решили поумничать и максимально запутать этот путь, но мы хитрее их и поэтому поищем обходную тропинку.
Нам известно, что существует как минимум еще одна проверка целостности, помимо sub_62FC28D4, которая заново перечитывает файл cbinrtl.dll (все данные, считанные функцией sub_62FC28D4, остаются локально внутри нее). Поэтому мы сразу по отработке sub_62FC28D4 ставим точку останова на ядерную функцию ReadFile, и — о чудо! — она тут же срабатывает на новом чтении файла cbinrtl.dll. Снова смотрим на стек вызовов.
Для просмотра ссылки Войдиили Зарегистрируйся
Очень интересно: выходит, на этот раз новорожденная cbinrtl.dll перепроверяет сама себя на невинность. Двигаясь по стеку вызовов вверх, мы обнаруживаем саму процедуру подсчета хеша.
Для просмотра ссылки Войдиили Зарегистрируйся
На скриншоте функция cbinrtl.7FF9985145B0 возвращает в регистре RAX адрес указателя на 512-битный хеш Whirlpool от модуля cbinrtl.dll (выделен в дампе). В этом случае запатчить проверку не так просто, в отличие от предыдущего случая, где хеши тупо сравнивались при помощи memcmp. Разработчики решили заморочиться по максимуму, проделывая над полученным хешем множество мудреных манипуляций, которые в итоге и приводят к неочевидному перемешиванию данных в структуре qword_62FC9180.
Мы же заморачиваться не будем, а тупо возьмем готовый 512-битный хеш от исходного файла cbinrtl.dll и подставим его в функцию cbinrtl.7FF9985145B0 вместо вычисления Whirlpool. Эксперимент показывает, что этого уже достаточно для того, чтобы отвязанная от проверки библиотека стартовала и работала корректно.
Итак, мы, особо не напрягаясь, при помощи всего трех патчей научились отламывать самую сильную и непроницаемую защиту VBA-кода от любого скомпилированного с ней модуля. Давай теперь посмотрим, что можно сделать с восстановлением VBA-кода из скомпилированного файла.
Разработчики пишут у себя на сайте, что компилятор DoneEx VbaCompiler преобразует исходный код VBA в код на языке C, а он затем компилируется в нативный DLL-файл Windows, за счет чего вроде бы повышается производительность выполнения кода. Это весьма похоже на истину, поскольку они для компиляции явно пользуются готовыми свободными компиляторами GCC и MinGW, входящими в комплект поставки.
Процесс компиляции можно отследить, прервав его выполнение и проанализировав содержимое рабочей папки, куда программа помещает преобразованные исходники на C, из которых компилятор собирает результирующую DLL. Но для детального анализа у нас осталось мало времени, поэтому попробуем кратко проанализировать его итог. Для этого возьмем первый из примеров, которые авторы любезно предлагают скачать Для просмотра ссылки Войдиили Зарегистрируйся. Пример hypocycloid — animated.xls представляет собой красивую демку, рисующую разноцветные гипоциклоиды с настраиваемыми случайными параметрами, которые можно анимировать. Выглядит это вот так.
Для просмотра ссылки Войдиили Зарегистрируйся
В примере представлены как исходный .XLS с VBA-кодом макроса, так и скомпилированный, содержащийся в подкаталоге Compiled. Для простоты рассмотрим код обработчика кнопки Toggle Animation, переключающей режим отображения со статического на анимированный. Исходный VBA-код этого обработчика выглядит так:
Смысл кода прост: при нажатии на кнопку проверить состояние флажка ChartIsAnimated — если он уже установлен (процесс анимации идет), то сбросить его. В противном случае установить и начать крутить цикл анимации, генерируя случайные гипоциклоиды до тех пор, пока флаг не будет сброшен очередным нажатием.
В скомпилированном коде на этом обработчике висит шлюз, перенаправляющий обработку на нативную функцию j5hth29ygo5zemw библиотеки hypocycloid_64.dll:
Подобные шлюзы висят на всех других обработчиках экспортов в VBA-код. Попробуем проанализировать код функции f8q8phtkxn. На первый взгляд, он виртуализирован и полностью состоит из косвенных табличных вызовов на функции библиотеки cbinrtl.dll. Помнишь, в начале статьи мы обратили внимание на то, что при вызове функции DummyFunc05 при загрузке книги инициализируется некая волшебная структура qword_62FC9180, содержащая в себе 55 указателей на функции, правильность порядка которых зависит от контрольной суммы файла cbinrtl.dll? Эти функции и представляют собой базовый набор виртуальных команд, на которые компилятор раскладывает синтаксические единицы исходного VBA-кода.
Прямые вызовы и константы (за исключением целочисленных) в декомпилированном коде тоже отсутствуют. Попробуем их восстановить при помощи уже используемого здесь отладчика х64dbg. Для этого запускаем макрос в анимированном режиме и приаттачиваемся отладчиком к процессу Excel, после чего ставим точку останова на функцию f8q8phtkxn. Нажимая кнопку Toggle Animation, проходим эту функцию отладчиком в обоих направлениях и восстанавливаем примерную логику ее работы:
Из этого уже можно делать выводы о принципах компиляции кода. Мы видим, что простые переменные и действия с ними полностью компилируются в натив, локальные — в локальные переменные, а глобальные — в глобальные. То же самое происходит и с простыми целочисленными константами. Как минимум это действительно дает выигрыш в скорости выполнения макроса. Остальные константы (включая строковые имена объектов и методов) находятся в таблицах, в которые расшифровываются во время загрузки макроса.
Вызовы функций и методов реализованы через вышеописанное немногочисленное множество виртуальных команд, логика которых проста и восстановима при желании до исходного VBA-кода. Дело упрощается еще и тем, что декомпиляция, отладка и патчинг скомпилированного кода ничем не затруднены.
В общем, подведя итоги анализа, можно сказать следующее. Защита, конечно, достаточно простенькая и ломается (особенно после прочтения этой статьи), как говорится, за чашечкой пива. Тем не менее в ней использованы достаточно остроумные решения, которые действительно помогут защитить VBA-код от других макросописателей, только что освоивших автоматизацию Excel.
Главная проблема продукта — крайняя неустойчивость работы: видимо, авторы испытывают большие сложности с тестированием, поэтому и сам компилятор, и скомпилированные в нем макросы работают на весьма узком подмножестве версий, в частности, у них чудовищные проблемы с локализованными версиями (видимо, из за особенностей обфускации имен функций и строковых констант).
Но авторы продолжают трудиться над улучшением своего продукта, с завидным упорством клепая новые версии чуть ли не каждые две недели. Поэтому, надеюсь, если моя статья попадется авторам на глаза, она хоть немного поможет им в совершенствовании их продукта.
Ты, вероятно, обращал внимание на забавный эффект: как только нуб выучит какой‑нибудь простецкий язык в достаточной степени, чтобы самому писать скрипты, его охватывает всепоглощающее чувство собственного величия, переходящее в желание продавать свои поделки окружающим. Этому обычно сопутствует опасение, как бы эту бесценную интеллектуальную собственность не украли. Ничего плохого в этом нет, поскольку такой страх дает работу целому сегменту программистов, пишущих защиты для скриптов разной степени упоротости.
Я уже писал статьи про подобные защиты, разработанные на JavaScript, Python, PHP и даже AutoIT. Сегодня у меня наконец‑то дошли руки до темы защиты экселевских VBA-скриптов.
Недавно я наткнулся на известный в очень узких кругах компилятор Для просмотра ссылки Войди
К слову сказать, эти ребята еще и Для просмотра ссылки Войди
Итак, предположим, что нам в руки попал необычный экселевский файл. К нему прилагается одна (или две) DLL-библиотека с тем же именем, но возможны суффиксы *_64 или *_32 в зависимости от разрядности. Если натравить на них Detect It Easy, программа показывает компилятор MinGW(GCC: (GNU)), однако ординалы из них экспортируются довольно подозрительные.
Для просмотра ссылки Войди
Попробуем открыть защищенный документ. На экране появляется весьма раздражающее окно с бегущим ползунком, а затем выскакивает предложение о регистрации.
Для просмотра ссылки Войди
Пока оно висит, откроем наш любимый отладчик x64dbg и приаттачимся к процессу Excel. Принудительно прервав его выполнение, мы видим занятный стек вызовов.
Для просмотра ссылки Войди
Из него как на ладони видна последовательность вызовов, порождающих это окно. Вначале непосредственно из VBA-кода вызывается функция SetThisWorkbook, экспортируемая из нашей скомпилированной библиотеки testvba1_xlsm_64.dll. Эта функция вызывает функцию DummyFunc05 некоей загадочной библиотеки cbinrtl.dll, отсутствующей в каталоге макроса и вообще непонятно откуда взявшейся. А уже она выкидывает окно предупреждения, реализованное через функцию DialogBoxIndirectParamW.
При детальном рассмотрении мы обнаруживаем эту библиотеку в подкаталоге v6n3vk66ej (название при каждом вызове случайное) временной папки Windows. Скомпилированный модуль testvba1_xlsm_64.dll сохраняет ее туда из соответствующего собственного ресурса при вызове SetThisWorkbook, а затем подчищает за собой при отработке. Собственно, скомпилированный модуль практически целиком и состоит из библиотеки cbinrtl.dll, хранящейся в нем в явном незашифрованном виде, так же как и в самом модуле компилятора vbaclr4e.exe, от которого ее и получает. Это дает надежду на убиение двух зайцев патчем файла testvba1_xlsm_64.dll — можно патчить код и библиотеки testvba1_xlsm_64.dll, и порождаемой ею библиотеки cbinrtl.dll.
Попробуем это реализовать. Сразу напрашивается гипотеза, что защита сосредоточена в функции DummyFunc05, — ведь именно ее вызов и выводит сообщение о незарегистрированной версии. Однако ее нельзя просто так взять и закоротить — Excel при этом падает с ошибкой. При ближайшем рассмотрении мы видим ее вызовы в IDA. Почему так происходит? Эта функция заполняет некую жизненно важную для программы структуру qword_62FC9180.
Для просмотра ссылки Войди
Значит, придется копать вглубь кода DummyFunc05, благо основные вызовы у нас уже размечены на стеке. Довольно быстро мы натыкаемся на развилку в коде cbinrtl.dll, которая ведет к заполнению структуры qword_62FC9180 (на следующем скриншоте она обозначена как a2, поскольку передается вторым параметром в процедуру).
Для просмотра ссылки Войди
Функция sub_180047A20 в незарегистрированной версии выкидывает окна c предупреждениями, однако, если скомпилировать модуль без защиты, эта функция просто возвращает 1 без лишних слов. Если в отладчике закоротить ее на return 1, все тоже работает безо всяких предупреждений, но при попытке патча модуля testvba1_xlsm_64.dll нас ждет сюрприз. Находим во вложенном в файл testvba1_xlsm_64.dll модуле cbinrtl.dll место, соответствующее sub_180047A20, и патчим его нужным образом.
Для просмотра ссылки Войди
Однако патченный файл упорно отказывается загружаться, хотя, как я уже говорил, при патче непосредственно в отладчике все работает корректно. Налицо встроенный контроль целостности. Попробуем его открутить.
Первое, что бросается в глаза, — проверка 128-битного хеша SHA-1 непосредственно перед загрузкой cbinrtl.dll. Обрати внимание на верхнюю часть скриншота — загрузка модуля происходит только тогда, когда функция sub_62FC28D4 дает добро.
Для просмотра ссылки Войди
Внутри она реализована так.
Для просмотра ссылки Войди
Функция sub_62FC27F7 читает весь модуль, sub_62FC2095 считает его хеш, который затем сравнивается с тестовым значением при помощи memcmp (его возвращает sub_62FC1720). Эту проверку легко можно закоротить, однако ей дело не ограничивается. Если мы это сделаем, то модуль cbinrtl.dll загружается, патченная функция DummyFunc05 корректно отрабатывает, однако в самом конце SetThisWorkbook вылетает по эксепшену при вызове некоего безымянного callback из того же cbinrtl.dll.
Для просмотра ссылки Войди
В непатченном варианте указатель на функцию qword_62FC90B0 содержит совершенно другой адрес, который отрабатывает нормально. Помнишь, в начале нашего повествования я упоминал некую жизненно важную структуру qword_62FC9180, заполняемую в DummyFunc05? Она как раз и содержит этот адрес и, если модуль пропатчен, заполняется некорректно. Разработчики решили поумничать и максимально запутать этот путь, но мы хитрее их и поэтому поищем обходную тропинку.
Нам известно, что существует как минимум еще одна проверка целостности, помимо sub_62FC28D4, которая заново перечитывает файл cbinrtl.dll (все данные, считанные функцией sub_62FC28D4, остаются локально внутри нее). Поэтому мы сразу по отработке sub_62FC28D4 ставим точку останова на ядерную функцию ReadFile, и — о чудо! — она тут же срабатывает на новом чтении файла cbinrtl.dll. Снова смотрим на стек вызовов.
Для просмотра ссылки Войди
Очень интересно: выходит, на этот раз новорожденная cbinrtl.dll перепроверяет сама себя на невинность. Двигаясь по стеку вызовов вверх, мы обнаруживаем саму процедуру подсчета хеша.
Для просмотра ссылки Войди
На скриншоте функция cbinrtl.7FF9985145B0 возвращает в регистре RAX адрес указателя на 512-битный хеш Whirlpool от модуля cbinrtl.dll (выделен в дампе). В этом случае запатчить проверку не так просто, в отличие от предыдущего случая, где хеши тупо сравнивались при помощи memcmp. Разработчики решили заморочиться по максимуму, проделывая над полученным хешем множество мудреных манипуляций, которые в итоге и приводят к неочевидному перемешиванию данных в структуре qword_62FC9180.
Мы же заморачиваться не будем, а тупо возьмем готовый 512-битный хеш от исходного файла cbinrtl.dll и подставим его в функцию cbinrtl.7FF9985145B0 вместо вычисления Whirlpool. Эксперимент показывает, что этого уже достаточно для того, чтобы отвязанная от проверки библиотека стартовала и работала корректно.
Итак, мы, особо не напрягаясь, при помощи всего трех патчей научились отламывать самую сильную и непроницаемую защиту VBA-кода от любого скомпилированного с ней модуля. Давай теперь посмотрим, что можно сделать с восстановлением VBA-кода из скомпилированного файла.
Разработчики пишут у себя на сайте, что компилятор DoneEx VbaCompiler преобразует исходный код VBA в код на языке C, а он затем компилируется в нативный DLL-файл Windows, за счет чего вроде бы повышается производительность выполнения кода. Это весьма похоже на истину, поскольку они для компиляции явно пользуются готовыми свободными компиляторами GCC и MinGW, входящими в комплект поставки.
Процесс компиляции можно отследить, прервав его выполнение и проанализировав содержимое рабочей папки, куда программа помещает преобразованные исходники на C, из которых компилятор собирает результирующую DLL. Но для детального анализа у нас осталось мало времени, поэтому попробуем кратко проанализировать его итог. Для этого возьмем первый из примеров, которые авторы любезно предлагают скачать Для просмотра ссылки Войди
Для просмотра ссылки Войди
В примере представлены как исходный .XLS с VBA-кодом макроса, так и скомпилированный, содержащийся в подкаталоге Compiled. Для простоты рассмотрим код обработчика кнопки Toggle Animation, переключающей режим отображения со статического на анимированный. Исходный VBA-код этого обработчика выглядит так:
Код:
Sub AnimateButton_Click()
Dim i As Integer
If ChartIsAnimated Then GoTo finish
ChartIsAnimated = True
On Error GoTo finish
Application.EnableCancelKey = xlErrorHandler
i = 1
Do
Range("V2").Value = i * Range("Speed") * 0.05
DoEvents
i = i + 1
If Not ChartIsAnimated Then GoTo finish
Loop
finish:
ChartIsAnimated = False
Range("V2").Value = 0
'End
End Sub
В скомпилированном коде на этом обработчике висит шлюз, перенаправляющий обработку на нативную функцию j5hth29ygo5zemw библиотеки hypocycloid_64.dll:
Код:
Private Declare PtrSafe Sub g5fb5vh8q Lib "hypocycloid_64.dll" Alias "j5hth29ygo5zemw" ()
Sub AnimateButton_Click()
g5fb5vh8q
End Sub
Открыв библиотеку hypocycloid_64.dll в IDA, мы обнаруживаем внутри экспортируемой функции j5hth29ygo5zemw еще один шлюз, передающий управление на другую функцию f8q8phtkxn:
__int64 j5hth29ygo5zemw()
{
jmp_buf Buf; // [rsp+30h] [rbp-108h] BYREF
memset(Buf, 0, sizeof(Buf));
qword_6A74B1C0();
if ( setjmp(Buf) )
{
if ( !(unsigned int)qword_6A74B188() )
return qword_6A74B0D0();
if ( (unsigned int)qword_6A74B188() == 1 )
qword_6A74B160(0);
}
else
{
qword_6A74B050(Buf);
}
qword_6A74B160(1);
f8q8phtkxn();
return qword_6A74B0D0();
}
Прямые вызовы и константы (за исключением целочисленных) в декомпилированном коде тоже отсутствуют. Попробуем их восстановить при помощи уже используемого здесь отладчика х64dbg. Для этого запускаем макрос в анимированном режиме и приаттачиваемся отладчиком к процессу Excel, после чего ставим точку останова на функцию f8q8phtkxn. Нажимая кнопку Toggle Animation, проходим эту функцию отладчиком в обоих направлениях и восстанавливаем примерную логику ее работы:
Код:
__int64 f8q8phtkxn()
{
memset(v15, 0, sizeof(v15));
memset(Buf, 0, sizeof(Buf));
qword_6A74B1C0();
// v15 = 0.05
qword_6A74B180(v15, 8);
if ( setjmp(Buf) )
{
if ( !(unsigned int)qword_6A74B188() )
return qword_6A74B0D0();
if ( (unsigned int)qword_6A74B188() == 1 )
qword_6A74B160(0);
}
else
{
qword_6A74B178(Buf);
}
// If ChartIsAnimated Then GoTo finish, word_6A74B1F4 — это ChartIsAnimated
if ( !word_6A74B1F4 )
{
// ChartIsAnimated = True
word_6A74B1F4 = -1;
// i = 1
v1 = 1;
// On Error GoTo finish
qword_6A74B160(1);
qword_6A74B190();
qword_6A74B158(qword_6A74B1E8);
// unk_6A74DC78="EnableCancelKey"
qword_6A74B150(unk_6A74DC78);
qword_6A74B148(2);
// Application.EnableCancelKey = xlErrorHandler=2
qword_6A74B140();
do
{
qword_6A74B190();
qword_6A74B158(qword_6A74B1E8);
// unk_6A74DC80 = "Range"
qword_6A74B150(unk_6A74DC80);
qword_6A74B138(0);
// unk_6A74DC08 = "V2"
qword_6A74B150(unk_6A74DC08);
qword_6A74B120();
// unk_6A74DC88 = "Value"
qword_6A74B150(unk_6A74DC88);
v4 = (void (__fastcall *)(__int64 *))qword_6A74B0E0;
v5 = (void (__fastcall *)(_QWORD *, __int64 *))qword_6A74B1A0;
v2 = (void (__fastcall *)(_QWORD *, _BYTE *, __int64 *))qword_6A74B108;
v6 = (void (__fastcall *)(_QWORD *, __int64, __int64 *))qword_6A74B100;
qword_6A74B190();
qword_6A74B158(qword_6A74B1E8);
// unk_6A74DC80 = "Range"
qword_6A74B150(unk_6A74DC80);
qword_6A74B138(0);
// unk_6A74DC10 = "Speed"
qword_6A74B150(unk_6A74DC10);
qword_6A74B120();
qword_6A74B0F8();
qword_6A74B0F0(v14);
v7 = v14[0];
v8 = v14[1];
v9 = v14[2];
v2(v10, &v15[24], &v7);
// i = i + 1
v3 = (unsigned int)(__int16)v1++;
v7 = v10[0];
v8 = v10[1];
v9 = v10[2];
// вызывает VarMul
v6(v11, v3, &v7);
v7 = v11[0];
v8 = v11[1];
v9 = v11[2];
v2(v12, v15, &v7);
v7 = v12[0];
v8 = v12[1];
v9 = v12[2];
// вызывает VarMul
v5(v13, &v7);
v7 = v13[0];
v8 = v13[1];
v9 = v13[2];
v4(&v7);
// Range("V2").Value = i * Range("Speed") * 0.05
qword_6A74B140();
// DoEvents
qword_6A74B1B0();
}
// while (ChartIsAnimated)
while ( word_6A74B1F4 == -1 );
}
// ChartIsAnimated = False
word_6A74B1F4 = 0;
qword_6A74B190();
qword_6A74B158(qword_6A74B1E8);
// unk_6A74DC80 = "Range"
qword_6A74B150(unk_6A74DC80);
qword_6A74B138(0);
// unk_6A74DC08 = "V2"
qword_6A74B150(unk_6A74DC08);
qword_6A74B120();
// unk_6A74DC88 = "Value"
qword_6A74B150(unk_6A74DC88);
qword_6A74B0D8(0);
// Range("V2").Value = 0
qword_6A74B140();
return qword_6A74B0D0();
}
Вызовы функций и методов реализованы через вышеописанное немногочисленное множество виртуальных команд, логика которых проста и восстановима при желании до исходного VBA-кода. Дело упрощается еще и тем, что декомпиляция, отладка и патчинг скомпилированного кода ничем не затруднены.
В общем, подведя итоги анализа, можно сказать следующее. Защита, конечно, достаточно простенькая и ломается (особенно после прочтения этой статьи), как говорится, за чашечкой пива. Тем не менее в ней использованы достаточно остроумные решения, которые действительно помогут защитить VBA-код от других макросописателей, только что освоивших автоматизацию Excel.
Главная проблема продукта — крайняя неустойчивость работы: видимо, авторы испытывают большие сложности с тестированием, поэтому и сам компилятор, и скомпилированные в нем макросы работают на весьма узком подмножестве версий, в частности, у них чудовищные проблемы с локализованными версиями (видимо, из за особенностей обфускации имен функций и строковых констант).
Но авторы продолжают трудиться над улучшением своего продукта, с завидным упорством клепая новые версии чуть ли не каждые две недели. Поэтому, надеюсь, если моя статья попадется авторам на глаза, она хоть немного поможет им в совершенствовании их продукта.