Содержание статьи
warning
Статья имеет ознакомительный характер и предназначена для исследователей безопасности и специалистов по разработке защиты программного обеспечения. Автор и редакция не несут ответственности за любой вред, причиненный с применением изложенной информации. Нарушение авторских прав и патентного законодательства, распространение контрафактного ПО и нарушение работы систем преследуются по закону.
Каждый уважающий себя хакер просто обязан знать врага в лицо и разбираться в основных криптоалгоритмах, популярных и не очень. Так, криптоподпись EdDSA, несмотря на довольно широкое использование (I2P, OpenBSD, OpenSSH и так далее), в отличие от своей эллиптической сестры EcDSA сильно обделена реализациями в стандартных криптобиблиотеках. Ее можно найти разве что в узкоспецифических проектах типа NaCl, SUPERCOP, python-ed25519 и прочей экзотике.
Сложностей добавляет и то, что по упомянутой причине каждый автор реализует хеширование, а также генерацию ключей и сигнатур по‑своему. Поэтому я и решил поподробнее рассказать об алгоритме EdDSA, причем именно с практической точки зрения. Мы разберем реализацию и работу этой подписи на конкретном примере.
Предмет исследования
Предметом нашего исследования станет некое приложение, при первом запуске скачивающее триальную лицензию с сервера. После несложных телодвижений в ProcMon скачанная лицензия благополучно обнаруживается в реестре.
![](https://static.xakep.ru/images/fabc145be97a0e835b693bc1e97e0517/37803/0.jpg)
Кажется, что все в ней прекрасно: в понятном текстовом виде хранится и версия, и ID оборудования, а также время начала и конца срока действия в миллисекундах. Можно смело править и пользоваться! Но нет, в конце мы обнаруживаем поле с недвусмысленным именем signature
, где в шестнадцатеричном виде закодирован некий 64-байтовый (512-битный) блок данных.
F8 BF BA E3 73 E5 AC 75 3F 38 AC DA F2 0C 42 B1 80 0C 18 A0 BF 92 8F 39 5C B1 A1 8E 2E E7 47 62
F1 3A E7 94 2A 5F 61 6D 97 5F 76 5B FD 82 B6 58 A0 D7 4C 07 3B 7E 61 A1 B3 85 75 62 93 94 90 06
Действительно ли это сигнатура или подпись текстовых данных лицензии, проверить элементарно: замена любого байта в текстовой части или в самой сигнатуре делает лицензию невалидной, и программа немедленно кидается повторно скачивать ее с удаленного сервера. Первый 512-битный хеш, который приходит в голову, — это SHA-512, его наличие подтверждает и программа Krypto Analyzer, если скормить ей наш исполняемый модуль.
То, что это не случайный набор байтов, а реальная инициализация подсчета хеша SHA-512, нам подтверждает и IDA, в которой мы открываем код приложения по любезно предоставленному нам анализатором адресу.
Однако счастье оказывается не так близко, как кажется: посчитанная контрольная сумма SHA-512 совершенно не похожа на значение signature
, а значит, для подписи не годится. Подпись считается каким‑то иным способом.
Попробуем копнуть с другой стороны: найти проверку подписи в отладчике. Загружаем нашу программу в x64dbg и ищем проверку подписи. Программа написана на чистом C++, ничем не защищена и свободна от антиотладчиков, поэтому искомое место находится элементарно, в псевдокоде IDA оно выглядит так.
![](https://static.xakep.ru/images/fabc145be97a0e835b693bc1e97e0517/37806/3.jpg)
Будь мы чуть более ленивыми, то закоротили бы эту функцию на return
(да, при успешной верификации она возвращает именно 0) и успокоились бы, но тогда смысла писать эту статью не было бы никакого. Попробуем все‑таки разобраться, как устроена сигнатура и как она верифицируется.
Очевидно, что финальную проверку выполняет функция sub_14000517D
, которая сложным и непрямым способом сравнивает на тождественное равенство две 32-байтовые последовательности. Первую из них IDA-шный декомпилятор условно обзывает a1 + 8. Она представляет собой первые 32 байта сигнатуры, то есть в нашем случае выглядит так:
F8 BF BA E3 73 E5 AC 75 3F 38 AC DA F2 0C 42 B1 80 0C 18 A0 BF 92 8F 39 5C B1 A1 8E 2E E7 47 62
Вторая последовательность вычисляется функцией sub_1401155E0
и условно называется a1 + 104. Оставим пока без внимания тревожный звоночек «почему проверяется только первая половина сигнатуры, зачем тогда вторая?» и тупо попробуем переподписать лицензию значением a1 + 104. То есть меняем в текстовой части лицензии любой байт, ставим точку останова на проверку, берем новое значение a1 + 104 и подставляем его вместо первых 32 байт сигнатуры.
План кажется надежным, как швейцарские часы, но он не работает: после замены первой половины сигнатуры значение a1 + 104 снова меняется. Это означает, что первая половина сигнатуры входит в обе части этого криптоуравнения. Вторая половина сигнатуры тоже находится довольно быстро — она под условным именем a1 + 40 является аргументом функций sub_1401162C0
и sub_140114D30
в цепочке весьма головоломного расчета проверочного значения a1 + 104.
Попробуем все‑таки опознать криптоалгоритм. Потыкавшись по функциям, ничего характерного не обнаруживаем, лишь какие‑то долгие, ломающие мозг вычисления, напоминающие операции с BigInteger, коими они, очевидно, и являются. Самой перспективной в этом плане представляется функция sub_1401161B0
.
Продолжение доступно только участникам
Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».
Присоединяйся к сообществу «Xakep.ru»!
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее