Издательский дом ООО "Гейм Лэнд"ЖУРНАЛ ХАКЕР #134, ФЕВРАЛЬ 2010 г.

Модифицируем подписанные библиотеки в .NET

Алексей Бойко (alexeybbb@gmail.com)


Задумался я тут над смыслом подписания компоновочных блоков .Net. Наверняка, ты тоже подписывал свои библиотеки, чтобы установить их в GAC. В ходе сегодняшнего расследования мы научимся изменять подписанные сборки, не обладая исходниками и секретными ключами.

Приватные сборки

При подписании библиотеки (назначении строгого имени) открытый ключ записывается в манифест. Таким образом, чтобы внести изменения в чужую подписанную библиотеку, нужно просто заменить публичный ключ на свой. Или — еще проще — сделать новую сборку с таким же именем и подписать на своем ключе. При использовании библиотеки любой публичный ключ будет принят как доверенный. Проведем эксперимент, создадим небольшую библиотечку:

Библиотека signedLib.dll

namespace signedLib
{
public class sLib
{
public static int GetNumber() { return 1; }
}
}

Подпишем ее и добавим к проекту консольного приложения:

Консольное приложение changeKey.exe

namespace changeKey
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(signedLib.sLib.GetNumber());
Console.ReadLine();
}
}
}

Затем скомпилируем релиз проекта. С помощью .NET Reflector [1] и плагина Reflexil [2] отредактируем IL-код подписанной библиотеки (signedLib.dll), так что GetNumber() будет возвращать не «1», а «2». Консольное приложение не заметило подмены и вывело «2». Вывод: подменить/изменить приватную сборку со строгим именем очень просто. Другие сборки, ссылающиеся на измененную, никак на это не реагируют, несмотря на то, что они были скомпилированы с оригинальной. Обращаю внимание, что речь идет именно о приватных сборках, со сборками в GAC дело обстоит иначе.

Сборки в GAC

Как мы только что убедились, подписанные приватные компоновочные блоки можно легко модифицировать. При этом необязательно обладать ни исходниками, ни секретным ключом, ни правами администратора.
Вносить изменения в сборки, установленные в GAC, не многим сложнее. В случае с приватными сборками их «подписанность» никакой роли не играет. Подпись не проверяется, а «полный идентификатор приватного компоновочного блока состоит из имени компоновочного блока и числового номера его версии» (из книги Э. Троелсена, если что :)).

Сборки, устанавливаемые в GAC, должны иметь так называемое строгое имя. Сборка получает строгое имя в момент ее подписания. Идентификаторы сборок в GAC дополняются параметрами публичного ключа, подписи проверяются. Замуровали, демоны! Что же это получается? Окружили со всех сторон:

  • Незаметно внести изменения не получится - подпись проверку не пройдет.
  • Свой публичный ключ не подсунешь - идентификатор сборки изменится.

Тем не менее, не нужно быть криптографом, чтобы все же изменить библиотеку в GAC. Требуется всего лишь обладать правами администратора и знать параметры утилиты sn.exe (страдальцы, не имеющие Студии, вручную используют стандартную утилиту sn.exe для подписания компоновочных блоков).

Итак, возьмем проект уже знакомой библиотеки signedLib.dll (смотри выше). Подпишем ее и установим в GAC.

gacutil.exe /i D:\projects\changeKey\signedLib\bin\Release\signedLib.dll

Добавим референс к консольному приложению changeKey.exe. Компилируем релиз, убеждаемся, что в папке с программой нет файла signedLib.dll (это значит, что сборка будет загружена из GAC). Запускаем changeKey.exe - приложение показывает «1».

С этого момента воображаем себя атакующими - у нас нет исходников, нет секретного ключа. Но надо, чтобы метод GetNumber() возвращал не 1, а 2. Структуру файлов ниже C:\Windows\assembly проводник Windows не показывает. Создадим псевдодиск, на который будет проецироваться нужный каталог:

subst b: C:\Windows\assembly

В проводнике появился диск B.

.Net сборки попадают в папку GAC_MSIL; находим нужную папку (ее название совпадает с названием .dll файла). Внутри будет еще одна папка, а в ней, наконец, signedLib.dll. Копируем signedLib.dll на рабочий стол.
С помощью замечательной программы .NET Reflector и не менее замечательного плагина Reflexil (на нашем диске все это хозяйство тебя уже заждалось) мы будем редактировать библиотеку. Предварительно перепишем токен публичного ключа и его значение в блокнот (пригодятся позже). Как мы уже знаем, публичный ключ записан в самой сборке, и теперь в этом можно окончательно убедиться.

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

Нажимаем «Remove Strong Name» - удалить цифровую подпись. Закрываем сборку (теоретически закрывать сборку нет необходимости и нам должен подойти вариант «Register it for verification skipping», однако у меня эта операция заканчивается ошибкой; к тому же, в обучающих целях лучше проделать все операции вручную).

Теперь у нас есть:

  • Измененная, не подписанная dll;
  • Публичный ключ оригинальной библиотеки.

Осталось установить ее в GAC. Для этого воспользуемся механизмом отложенной подписи. Если сборка содержит информацию о публичном ключе, но не имеет цифровой подписи – говорят, что она имеет отложенную подпись (придумал это какой-то надмозг из Майкрософт «с целью тестирования»).

Сделать такую сборку с помощью .NET Reflector не составляет никакой сложности - нужно заполнить соответствующие поля, они выделены желтым на рисунке «Параметры публичного ключа» (помнишь, мы копировали их значения в блокнот?). И не забудь поставить галочку «HasPublicKey» (в теории публичный ключ нужно извлекать из секретного с помощью утилиты sn.exe и потом с помощью нее же создавать отложенную подпись).
Итак, мы получили сборку, которая называется так же, как оригинальная, имеет такую же версию и такой же публичный ключ. Получается, если ее установить в GAC, она получит точно такой же идентификатор, что и оригинальная (смотри начало статьи). Как я писал выше, по умолчанию у сборок в GAC проверяется подпись, однако проверку подписи можно отключить - опять же «для тестирования».

Чтобы отключить проверку подписи dll на данном компьютере, нужно воспользоваться sn.exe:

sn -Vr C:\Users\Alex\Desktop\signedLib.dll

Удаляем оригинальную сборку из GAC:

gacutil /u signedLib,Version=1.0.0.0,Culture=neutral,PublicKeyToken=2b1b71846e76146e

И устанавливаем измененную:

gacutil /i C:\Users\Alex\Desktop\signedLib.dll

Радуемся, глядя на выведенную gacutil.exe надпись:

Assembly successfully added to the cache

Вот мы и добились желаемого - изменили библиотеку, установленную в GAC. Чтобы еще раз порадоваться (и проверить результат), запускаем наше приложение changeKey.exe, которое в начале статьи выводило 1. Ура, теперь он покажет 2!

Подведем итог

Самое время подвести итог нашим сегодняшним свершениям. Сделаем это по пунктам:

  • Публичный ключ записан в самой сборке (точнее, в манифесте);
  • В случае с приватными сборками подписи не проверяются.

Чтобы изменить сборку в CAG, нужно:

  1. Сделать копию нужного dll-файла из C:\Windows\assembly (воспользовавшись командой subst).
  2. Извлечь из сборки публичный ключ.
  3. Модифицировать IL-код сборки и удалить цифровую подпись.
  4. Добавить к измененному файлу публичный ключ, полученный на шаге 2 (создадим отложенную подпись).
  5. Отменить проверку цифровой подписи для модифицированной сборки на данном компьютере.
  6. Удалить оригинальную сборку из GAC.
  7. Установить модифицированную сборку.

Для реализации шагов 5-7 нужно обладать правами администратора. Вот и все! На этом позволь откланяться и пожелать тебе огромных творческих узбеков на ниве исследований программного обеспечения.

DVD

Упомянутый в статье софт и некоторые дополнительные ништяки от автора ты можешь найти на нашем диске.

INFO

Для комфортного чтения статьи нужно обладать базовыми знаниями в области криптографии с открытым ключом.

Содержание